content/versions/1.13.1/guides/ugbtb.html (5,621 lines of code) (raw):
<!doctype html>
<html class="no-js" lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Beyond the Basics</title>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<!-- No caching headers -->
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="pragma" content="no-cache" />
<meta http-equiv="expires" content="-1" />
<!-- TODO: need to (re)instate CDN in the future (not using for now just so can develop off-line -->
<link href="../css/foundation/5.5.1/foundation.css" rel="stylesheet" />
<script src="../js/foundation/5.5.1/vendor/modernizr.js"></script>
<link href="../css/asciidoctor/colony.css" rel="stylesheet">
<link href="../css/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet">
<link href="../css/github-fork-ribbon-css/0.1.1/gh-fork-ribbon.css" rel="stylesheet" />
<!--[if lt IE 9]>
<link href="../css/github-fork-ribbon-css/0.1.1/gh-fork-ribbon.ie.css" rel="stylesheet" />
<![endif]-->
<style type="text/css">
pre code {
background-color: inherit;
border-style: none;
}
pre code > span:first-child {
margin-left: -5px;
}
<style>
<!--
<style type="text/css">
/* Stylesheet for CodeRay to match GitHub theme | MIT License | http://foundation.zurb.com */
/*pre.CodeRay {background-color:#f7f7f8;}*/
.CodeRay .line-numbers{border-right:1px solid #d8d8d8;padding:0 0.5em 0 .25em}
.CodeRay span.line-numbers{display:inline-block;margin-right:.5em;color:rgba(0,0,0,.3)}
.CodeRay .line-numbers strong{color:rgba(0,0,0,.4)}
table.CodeRay{border-collapse:separate;border-spacing:0;margin-bottom:0;border:0;background:none}
table.CodeRay td{vertical-align: top;line-height:1.45}
table.CodeRay td.line-numbers{text-align:right}
table.CodeRay td.line-numbers>pre{padding:0;color:rgba(0,0,0,.3)}
table.CodeRay td.code{padding:0 0 0 .5em}
table.CodeRay td.code>pre{padding:0}
.CodeRay .debug{color:#fff !important;background:#000080 !important}
.CodeRay .annotation{color:#007}
.CodeRay .attribute-name{color:#000080}
.CodeRay .attribute-value{color:#700}
.CodeRay .binary{color:#509}
.CodeRay .comment{color:#998;font-style:italic}
.CodeRay .char{color:#04d}
.CodeRay .char .content{color:#04d}
.CodeRay .char .delimiter{color:#039}
.CodeRay .class{color:#458;font-weight:bold}
.CodeRay .complex{color:#a08}
.CodeRay .constant,.CodeRay .predefined-constant{color:#008080}
.CodeRay .color{color:#099}
.CodeRay .class-variable{color:#369}
.CodeRay .decorator{color:#b0b}
.CodeRay .definition{color:#099}
.CodeRay .delimiter{color:#000}
.CodeRay .doc{color:#970}
.CodeRay .doctype{color:#34b}
.CodeRay .doc-string{color:#d42}
.CodeRay .escape{color:#666}
.CodeRay .entity{color:#800}
.CodeRay .error{color:#808}
.CodeRay .exception{color:inherit}
.CodeRay .filename{color:#099}
.CodeRay .function{color:#900;font-weight:bold}
.CodeRay .global-variable{color:#008080}
.CodeRay .hex{color:#058}
.CodeRay .integer,.CodeRay .float{color:#099}
.CodeRay .include{color:#555}
.CodeRay .inline{color:#000}
.CodeRay .inline .inline{background:#ccc}
.CodeRay .inline .inline .inline{background:#bbb}
.CodeRay .inline .inline-delimiter{color:#d14}
.CodeRay .inline-delimiter{color:#d14}
.CodeRay .important{color:#555;font-weight:bold}
.CodeRay .interpreted{color:#b2b}
.CodeRay .instance-variable{color:#008080}
.CodeRay .label{color:#970}
.CodeRay .local-variable{color:#963}
.CodeRay .octal{color:#40e}
.CodeRay .predefined{color:#369}
.CodeRay .preprocessor{color:#579}
.CodeRay .pseudo-class{color:#555}
.CodeRay .directive{font-weight:bold}
.CodeRay .type{font-weight:bold}
.CodeRay .predefined-type{color:inherit}
.CodeRay .reserved,.CodeRay .keyword {color:#000;font-weight:bold}
.CodeRay .key{color:#808}
.CodeRay .key .delimiter{color:#606}
.CodeRay .key .char{color:#80f}
.CodeRay .value{color:#088}
.CodeRay .regexp .delimiter{color:#808}
.CodeRay .regexp .content{color:#808}
.CodeRay .regexp .modifier{color:#808}
.CodeRay .regexp .char{color:#d14}
.CodeRay .regexp .function{color:#404;font-weight:bold}
.CodeRay .string{color:#d20}
.CodeRay .string .string .string{background:#ffd0d0}
.CodeRay .string .content{color:#d14}
.CodeRay .string .char{color:#d14}
.CodeRay .string .delimiter{color:#d14}
.CodeRay .shell{color:#d14}
.CodeRay .shell .delimiter{color:#d14}
.CodeRay .symbol{color:#990073}
.CodeRay .symbol .content{color:#a60}
.CodeRay .symbol .delimiter{color:#630}
.CodeRay .tag{color:#008080}
.CodeRay .tag-special{color:#d70}
.CodeRay .variable{color:#036}
.CodeRay .insert{background:#afa}
.CodeRay .delete{background:#faa}
.CodeRay .change{color:#aaf;background:#007}
.CodeRay .head{color:#f8f;background:#505}
.CodeRay .insert .insert{color:#080}
.CodeRay .delete .delete{color:#800}
.CodeRay .change .change{color:#66f}
.CodeRay .head .head{color:#f4f}
pre.CodeRay code {
background-color: inherit;
border-style: none;
}
pre.CodeRay code > span:first-child {
margin-left: -5px;
}
.literalblock pre,
.listingblock pre:not(.highlight),
.listingblock pre[class="highlight"],
.listingblock pre[class^="highlight "],
.listingblock pre.CodeRay,
.listingblock pre.prettyprint {
background: rgb(253, 250, 246);
}
.sidebarblock .literalblock pre,
.sidebarblock .listingblock pre:not(.highlight),
.sidebarblock .listingblock pre[class="highlight"],
.sidebarblock .listingblock pre[class^="highlight "],
.sidebarblock .listingblock pre.CodeRay,
.sidebarblock .listingblock pre.prettyprint {
background: rgb(253, 250, 246);
}
<style>
-->
<style>
.github-fork-ribbon-wrapper.right {
position: fixed;
}
.github-fork-ribbon {
background: #090;
}
.github-fork-ribbon a:hover {
background:#0D0;
color:#fff;
font-size: 1.1em;
}
</style>
<style>
@media only screen and (min-width: 40.063em) {
.top-bar {
.contain-to-grid .top-bar {
max-width: 80rem;
}
}
}
.row {
max-width: 80rem;
}
</style>
<style>
.extended-quote,
.extended-quote-first {
margin-left: 40px;
margin-right: 40px;
font-style: italic;
}
.extended-quote-attribution {
text-align: right;
margin-right: 100px;
color: #10B061;
}
.extended-quote-first:before {
content: "\201c";
float: left;
font-size: 2.75em;
font-weight: bold;
line-height: 0.6em;
margin-left: -0.6em;
color: #003b6b;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
</style>
<style>
body {
position: relative;
}
*:not(pre) a > code {
color: #210DDC;
}
*:not(pre) > code {
background-color: inherit;
border: none;
font-weight: normal;
}
body div#toc li,
body div#toc2 li {
list-style-type: none;
}
div#doc-content {
margin-top: 30px;
}
div.documentation-page table.frame-all {
border-left: none;
border-right: none;
}
body div#toc li.active-region:before,
body div#toc2 li.active-region:before {
content: "\00BB \0020";
margin-left: -12px;
}
body div#toc li a.active,
body div#toc2 li a.active {
color: red;
}
body div#toc.toc,
body div#toc.toc2 {
position: fixed;
left: auto;
padding-top: 60px;
z-index: auto;
background-color: white;
border-left-color: #eee;
border-left-style: solid;
border-right: none;
min-height: 2000px;
}
</style>
<style>
@media only screen and (min-width: 768px) {
#toc.toc2 ul ul { margin-left: -10px; }
}
body div#toc .tocify-subheader ul {
margin-bottom: 0px;
}
body div#toc .tocify-subheader li {
font-size: 14px;
}
.tocify li.tocify-item, .tocify ul.tocify-item {
line-height: 24px;
}
body div#toc li.tocify-item.active:before,
body div#toc2 li.tocify-item.active:before {
content: "\00BB \0020";
margin-left: -12px;
}
body div#toc li.tocify-item.active a,
body div#toc2 li.tocify-item.active a {
color: red;
}
</style>
<style>
footer {
margin-top: 1000px;
}
</style>
<style>
/* overriding colony.css stylesheet */
.literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] {
/*padding: 1.25em 1.5625em 1.125em 1.5625em;*/
padding: 0.3em 0.6em 0.25em 0.6em;
}
@media only screen and (min-width: 1280px)
#toc.toc2 {
/*width: 20em;*/
width: 25em;
}
#doc-content a {
color: #210DDC;
}
.top-bar h1 {
border-bottom: inherit;
}
h2 {
margin-top: 80px;
}
h3 {
margin-top: 40px;
}
h4,h5 {
margin-top: 30px;
}
.admonitionblock.tip > table td.content {
color: #10B061;
}
.admonitionblock.note > table td.content {
color: #B509AB;
}
.admonitionblock.important > table td.content {
color: #D5810A;
}
.admonitionblock .title {
font-size: larger;
font-style: italic;
}
.imageblock img {
margin-bottom: 10px;
}
</style>
<style>
/* from http://ben.balter.com/2014/03/13/pages-anchor-links/ */
.header-link {
position: absolute;
left: -0.5em;
opacity: 0;
/*
-webkit-transition: opacity 0.2s ease-in-out 0.1s;
-moz-transition: opacity 0.2s ease-in-out 0.1s;
-ms-transition: opacity 0.2s ease-in-out 0.1s;
*/
}
h2:hover .header-link,
h3:hover .header-link,
h4:hover .header-link,
h5:hover .header-link,
h6:hover .header-link {
opacity: 1;
}
</style>
<style>
.top-bar
{
-webkit-transition-duration: .5s;
transition-duration: .5s;
-webkit-transition-timing-function: cubic-bezier( 0.215, 0.610, 0.355, 1.000 );
transition-timing-function: cubic-bezier( 0.215, 0.610, 0.355, 1.000 );
-webkit-transition-property: -webkit-transform;
transition-property: transform;
}
/*
http://osvaldas.info/auto-hide-sticky-header
MIT license
*/
.header--hidden
{
-webkit-transform: translateY( -100% );
-ms-transform: translateY( -100% );
transform: translateY( -100% );
transition-duration: .5s;
transition-timing-function: cubic-bezier( 0.215, 0.610, 0.355, 1.000 );
-webkit-transition-property: -webkit-transform;
transition-property: transform;
}
</style>
<style>
#doc-content a.guide {
color: white;
}
</style>
<style>
.tocify {
margin-top: 80px;
}
</style>
</script>
</head>
<body>
<<div class="github-fork-ribbon-wrapper right" style="position: fixed;">
<div class="github-fork-ribbon">
<a href="https://github.com/apache/isis#fork-destination-box">Fork me on GitHub</a>
</div>
</div>
<div class="row">
<div class="fixed contain-to-grid header">
<nav class="top-bar" data-topbar role="navigation" style="max-width: 80rem">
<ul class="title-area">
<li class="name">
<h1>
<a href="/index.html">Apache Isis™</a>
</h1>
</li>
<!-- Remove the class "menu-icon" to get rid of menu icon. Take out "Menu" to just have icon alone -->
<li class="toggle-topbar menu-icon"><a href="#"><span>Menu</span></a></li>
</ul>
<section class="top-bar-section">
<ul class="right">
<li class="has-form">
<FORM class="searchbox navbar-form navbar-right" id="searchbox_012614087480249044419:dn-q5gtwxya" action="http://www.google.com/cse">
<div class="row collapse">
<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>
</li>
</ul>
<!-- Left Nav Section -->
<ul class="left">
<li><a href="/documentation.html">Documentation</a></li>
<li><a href="/downloads.html">Downloads</a></li>
<li><a href="/help.html">Help</a></li>
<li><a href="/asf.html">@ASF</a></li>
</ul>
</section>
</nav>
</div>
</div>
<div class="row">
<div id="doc-content-left" class="large-9 medium-9 columns">
<div id="doc-content">
<div class="sect1">
<h2 id="_ugbtb">1. Beyond the Basics</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This guide provides <a href="#_ugbtb_other-techniques">more advanced</a> guidance on writing maintainable larger
applications.</p>
</div>
<div class="paragraph">
<p>Later chapters discuss how to <a href="#_ugbtb_deployment">deploy</a> your app, and discuss other ways in which you can <a href="#">extend</a> or adapt the framework itself to your particular needs.</p>
</div>
<div class="sect2">
<h3 id="_other_guides">1.1. Other Guides</h3>
<div class="paragraph">
<p>Apache Isis documentation is broken out into a number of user, reference and "supporting procedures" guides.</p>
</div>
<div class="paragraph">
<p>The user guides available are:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="ugfun.html">Fundamentals</a></p>
</li>
<li>
<p><a href="ugvw.html">Wicket viewer</a></p>
</li>
<li>
<p><a href="ugvro.html">Restful Objects viewer</a></p>
</li>
<li>
<p><a href="ugvro.html">DataNucleus object store</a></p>
</li>
<li>
<p><a href="ugsec.html">Security</a></p>
</li>
<li>
<p><a href="ugtst.html">Testing</a></p>
</li>
<li>
<p><a href="#">Beyond the Basics</a> (this guide)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The reference guides are:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="rgant.html">Annotations</a></p>
</li>
<li>
<p><a href="rgsvc.html">Domain Services</a></p>
</li>
<li>
<p><a href="rgcfg.html">Configuration Properties</a></p>
</li>
<li>
<p><a href="rgcms.html">Classes, Methods and Schema</a></p>
</li>
<li>
<p><a href="rgmvn.html">Apache Isis Maven plugin</a></p>
</li>
<li>
<p><a href="rgfis.html">Framework Internal Services</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The remaining guides are:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="dg.html">Developers' Guide</a> (how to set up a development environment
for Apache Isis and contribute back to the project)</p>
</li>
<li>
<p><a href="cgcom.html">Committers' Guide</a> (release procedures and related practices)</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_ugbtb_view-models">2. View Models</h2>
<div class="sectionbody">
<div class="paragraph">
<p>View models are a type of domain object (with state, behaviour etc) but where the state is <em>not</em> persisted into the
JDO/DataNucleus-managed database, but is instead converted to/from a string memento, and held by the calling client.
This opens up a number of more advanced use cases.</p>
</div>
<div class="paragraph">
<p>In this topic we’ll explore those use cases, and learn the programming model and conventions to use view models in your application.</p>
</div>
<div class="sect2">
<h3 id="_ugbtb_view-models_use-cases">2.1. Use Cases</h3>
<div class="paragraph">
<p>When developing an Apache Isis application you will most likely start off with the persistent domain entities:
<code>Customer</code>, <code>Order</code>, <code>Product</code>, and so on. For some applications this may well suffice. However, if the application
needs to integrate with other systems, or if the application needs to support reasonably complex business processes, then you may need to look beyond just domain entities. This section explores these use cases.</p>
</div>
<div class="sect3">
<h4 id="_ugbtb_view-models_use-cases_externally-managed-entities">2.1.1. Externally-managed entities</h4>
<div class="paragraph">
<p>Sometimes the entities that make up your application are persisted not in the local JDO/DataNucleus database
but reside in some other system, for example accessible only through a SOAP web service. Logically that data
might still be considered a domain entity and we might want to associate behaviour with it, however it cannot be
modelled as a domain entity if only because JDO/DataNucleus doesn’t know about the entity nor how to retrieve or
update it.</p>
</div>
<div class="paragraph">
<p>There are a couple of ways around this: we could either replicate the data somehow from the external system into the
Isis-managed database (in which case it is once again just another domain entity), or we could set up a stub/proxy for
the externally managed entity. This proxy would hold the reference to the externally-managed domain entity (eg an
external id), as well as the "smarts" to know how to interact with that entity (by making SOAP web service calls etc).</p>
</div>
<div class="paragraph">
<p>The stub/proxy is a type of view model: a view - if you like - onto the domain entity managed by the external system.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>DataNucleus does in fact define its own <a href="http://www.datanucleus.org/documentation/extensions/store_manager.html">Store Manager</a> extension point, so an alternative architecture would be to implement this interface such that DataNucleus
could make the calls to the external system; these externally-persisted domain entities would therefore be modelled as regular <code>@PersistenceCapable</code> entities after all. For entities not persisted externally the implementation would delegate down to the default RDBMS-specific <code>StoreManager</code> provided by DataNucleus itself.</p>
</div>
<div class="paragraph">
<p>An implementation that supported only reading from an external entity ought to be comparatively straight-forward, but
implementing one that also supported updating external entities would need to carefully consider error conditions if the
external system is unavailable; distributed transactions are most likely difficult/impossible to implement (and not
desirable in any case).</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_ugbtb_view-models_use-cases_in-memory-entities">2.1.2. In-memory entities</h4>
<div class="paragraph">
<p>As a variation on the above, sometimes there are domain objects that are, conceptually at least entities, but whose
state is not actually persisted anywhere, merely held in-memory (eg in a hash).</p>
</div>
<div class="paragraph">
<p>A simple example might be read-only configuration data that is read from a config file (eg log4j appender
definitions) but thereafter is presented in the UI just like any other entity.</p>
</div>
</div>
<div class="sect3">
<h4 id="_ugbtb_view-models_use-cases_application-layer-view-models">2.1.3. Application-layer view models</h4>
<div class="paragraph">
<p>Domain entities (whether locally persisted using JDO/DataNucleus or managed externally) are the bread-and-butter of Apache Isis applications: the focus after all, should be on the business domain concepts and ensuring that they are
solid. Generally those domain entities will make sense to the business domain experts: they form the <em>ubiquitous language</em> of the domain. These domain entities are part of the domain layer.</p>
</div>
<div class="paragraph">
<p>That said, it may not always be practical to expect end-users of the application to interact solely with those domain
entities. For example, it may be useful to show a dashboard of the most significant data in the system to a user,
often pulling in and aggregating information from multiple points of the app. Obtaining this information by hand (by
querying the respective services/repositories) would be tedious and slow; far better to have a dashboard do the job for
the end user.</p>
</div>
<div class="paragraph">
<p>A dashboard object is a model of the most relevant state to the end-user, in other words it is (quite literally) a view
model. It is not a persisted entity, instead it belongs to the application layer.</p>
</div>
<div class="paragraph">
<p>A view model need not merely aggregate data; it could also provide actions of its own. Most likely these actions will
be queries and will always ultimately just delegate down to the appropriate domain-layer service/repository. But in
some cases such view model actions might also modify state of underlying domain entities.</p>
</div>
<div class="paragraph">
<p>Another common use for view models is to help co-ordinate complex business processes; for example to perform a
quarterly invoicing run, or to upload annual interest rates from an Excel spreadsheet. In these cases the view model
might have some state of its own, but in most cases that state does not need to be persisted per se.</p>
</div>
<div class="sidebarblock">
<div class="content">
<div class="title">Desire Lines</div>
<div class="paragraph">
<p>One way to think of application view models is as modelling the "desire line": the commonly-trod path
that end-users must follow to get from point A to point B as quickly as possible.</p>
</div>
<div class="paragraph">
<p>To explain: there are <a href="http://ask.metafilter.com/62599/Where-the-sidewalk-ends">documented</a>
<a href="https://sivers.org/walkways">examples</a>
<a href="http://www.softpanorama.org/People/Wall/larry_wall_articles_and_interviews.shtml">that</a> architects of university
campus will only add in paths some while after the campus buildings are complete: let the pedestrians figure out the
routes they want to take. The name we like best for this idea is "desire lines", though it has also been called
a "desire path", "paving the path" or "paving the sidewalk".</p>
</div>
<div class="paragraph">
<p>What that means is you should add view models <em>after</em> having built up the domain layer, rather than before. These view
models pave that commonly-trod path, automating the steps that the end-user would otherwise have to do by hand.</p>
</div>
<div class="paragraph">
<p>It takes a little practice though, because even when building the domain layer "first", you should still bear in mind
what the use cases are that those domain entities are trying to support. You certainly <em>shouldn’t</em> try to build out a
domain layer that could support every conceivable use case before starting to think about view models.</p>
</div>
<div class="paragraph">
<p>Instead, you should iterate. Identify the use case/story/end-user objective that you will deliver value to the
business. Then build out the minimum domain entities to support that use case (refining the <a href="ugfun.html#_ugfun_core-concepts_philosophy_domain-driven-design_ubiquitous-language">ubiquitous language</a> as you
go). Then, identify if there any view models that could be introduced which would simplify the end-user interactions
with the system (perhaps automating several related use cases together).</p>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_ugbtb_view-models_use-cases_dtos">2.1.4. DTOs</h4>
<div class="paragraph">
<p>DTOs (data transfer objects) are simple classes that (according to <a href="https://en.wikipedia.org/wiki/Data_transfer_object">wikipedia</a>) "carry data between processes".</p>
</div>
<div class="paragraph">
<p>If those two processes are parts of the same overall application (the same team builds and deploys both server and
client) then there’s generally no need to define a DTO; just access the entities using Apache Isis'
<a href="ugvro.html">RestfulObjects viewer</a>.</p>
</div>
<div class="paragraph">
<p>On the other hand, if the client consuming the DTO is a different application — by which we mean developed/deployed by
a different (possible third-party) team — then the DTOs act as a formal contract between the provider and the consumer.
In such cases, exposing domain entities over <a href="ugvro.html">RestfulObjects</a> would be
"A Bad Thing"™ because the consumer would in effect have access to implementation details that could then not be
easily changed by the producer.</p>
</div>
<div class="paragraph">
<p>To support this use case, a view model can be defined such that it can act as a DTO. This is done by annotating the
class using JAXB annotations; this allows the consumer to obtain the DTO in XML format along with a corresponding
XSD schema describing the structure of that XML. A discussion of how that might be done using an ESB such as
<a href="http://camel.apache.org">Apache Camel™</a> follows <a href="#_ugbtb_view-models_use-cases_dtos_consumers">below</a>.</p>
</div>
<div class="paragraph">
<p>In case it’s not obvious, these DTOs are still usable as "regular" view models; they will render in the <a href="ugvw.html">Wicket viewer</a> just like any other. In fact (as the <a href="#_ugbtb_view-models_programming-model">programming model</a> section below makes clear), these JAXB-annotated view models are in many regards the most powerful of all the alternative ways of writing view models.</p>
</div>
<div class="paragraph">
<p>It’s also worth noting that it is also possible to download the XML (or XSD) straight from the UI, useful during development.
The view model simply needs to implement the <a href="rgcms.html#_rgcms_classes_mixins_Dto"><code>Dto</code></a> marker interface; the
framework has <a href="rgcms.html#_rgcms_classes_mixins_Dto">mixins</a> that contribute the download actions to the view model.</p>
</div>
<div class="sect4">
<h5 id="_ugbtb_view-models_use-cases_dtos_consumers">DTO Consumers</h5>
<div class="paragraph">
<p>The actual consumers of DTOs will generally obtain the XML of the view models either by requesting the XML directly,
eg using the <a href="ugvro.html">RestfulObjects viewer</a>, or may have the XML sent to them asynchronously using an ESB
such as Apache Camel.</p>
</div>
<div class="paragraph">
<p>In the former case, the consumer requests the DTO by calling the REST API with the appropriate HTTP <code>Accept</code> header.
An appropriate implementation of <a href="rgsvc.html#_rgsvc_spi_ContentMappingService"><code>ContentMappingService</code></a> can then be
used to return the appropriate DTO (as XML).</p>
</div>
<div class="paragraph">
<p>For the latter case, one design is simply for the application to instantiate the view model, then call the
<a href="rgsvc.html#_rgsvc_api_JaxbService"><code>JaxbService</code></a> to obtain its corresponding XML. This can then be published onto
the ESB, for example using an <a href="http://activemq.apache.org">Apache ActiveMQ ™</a> queue.</p>
</div>
<div class="paragraph">
<p>However, rather than try to push all the data that might be needed by any of these external systems in a single XML event
(which would require anticipating all the requirements, likely a hopeless task), a better design is to publish only
the fact that something of note has changed - ie, that an action on a domain object has been invoked - and then let the consumers call back to obtain other information if required. This can once again be done by calling the REST API with
an appropriate HTTP <code>Accept</code> header.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>This is an example of the <a href="https://leanpub.com/camel-design-patterns">VETRO pattern</a> (validate, enrich, transform, route, operate). In our case we focus on the validation (to determine the nature of the inbound message, ie which action was
invoked), and the enrich (callback to obtain a DTO with additional information required by the consumer).</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The (non-ASF) <a href="http://github.com/isisaddons/isis-module-publishmq">Isis addons' publishmq</a> module provides an out-of-the-box solution of this design. It provides an implementation of the <a href="rgsvc.html#_rgsvc_spi_PublishingService"><code>PublishingService</code></a>,
but which simply publishes instances of <a href="rgcms.html#_rgcms_schema-aim"><code>ActionInvocationMemento</code></a> to an ActiveMQ
queue. Camel (or similar) can then be hooked up to consume these events from this queue, and use a processor to
parse the action memento to determine what has changed on the source system. Thereafter, a subsequent Camel processor
can then call back to the source - via the <a href="#ugvro.adoc">Restful Objects viewer</a> - to enrich the message with
additional details using a DTO.</p>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_view-models_programming-model">2.2. Programming Model</h3>
<div class="paragraph">
<p>So much for the theory; how should view models be implemented? Fundamentally all view models' state is serialized into
a string memento; this memento is then held by the client (browser) in the form of a URL. As you might imagine, this
URL can become quite long, but Apache Isis offers a mechanism (the <a href="rgsvc.html#_rgsvc_spi_UrlEncodingService"><code>UrlEncodingService</code></a>) if it exceeds the maximum length for a URL
(2083 characters). Also, of course, this string memento must only contain characters that it is valid for use within
a URL.</p>
</div>
<div class="paragraph">
<p>While the underlying technique is the same irrespective of use case, the programming model provides various ways of
defining a view model so that the original intent is not lost. They are:</p>
</div>
<table class="tableblock frame-all grid-all spread">
<caption class="title">Table 1. View model programming model</caption>
<colgroup>
<col style="width: 14.2857%;">
<col style="width: 57.1428%;">
<col style="width: 28.5715%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Use case</th>
<th class="tableblock halign-left valign-top">Code</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>External entity</p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@DomainObject</span>(nature=Nature.EXTERNAL_ENTITY)
<span class="directive">public</span> <span class="type">class</span> <span class="class">CustomerRecordOnSAP</span> { ... }</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>Annotated with <a href="rgant.html#_rgant-DomainObject_nature"><code>@DomainObject#nature()</code></a> and a nature of <code>EXTERNAL_ENTITY</code>, with memento derived automatically from the properties of the domain object. Collections are ignored, as are any properties annotated as <a href="rgant.html#_rgant-Property_notPersisted">not persisted</a>.</p>
</div></div></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>In-memory entity</p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@DomainObject</span>(nature=Nature.INMEMORY_ENTITY)
<span class="directive">public</span> <span class="type">class</span> <span class="class">Log4JAppender</span> { ... }</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>As preceding, but using a nature of <code>INMEMORY_ENTITY</code>.</p>
</div></div></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>Application view model</p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@DomainObject</span>(nature=Nature.VIEW_MODEL)
<span class="directive">public</span> <span class="type">class</span> <span class="class">Dashboard</span> { ... }</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>As preceding, but using a nature of <code>VIEW_MODEL</code>.</p>
</div></div></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>Application view model</p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@ViewModel</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">Dashboard</span> { ... }</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>Annotated with <a href="rgant.html#_rgant-ViewModel"><code>@ViewModel</code></a> annotation (effectively just an alias)' memento is as preceding: from "persisted" properties, collections ignored</p>
</div></div></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>Application view model</p>
</div></div></td>
<td class="tableblock halign-left valign-top"><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">ExcelUploadManager</span> <span class="directive">implements</span> ViewModel {
<span class="directive">public</span> <span class="predefined-type">String</span> viewModelMemento() { ... }
<span class="directive">public</span> <span class="type">void</span> viewModelInit(<span class="predefined-type">String</span> memento) { ... }
}</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>Implement <a href="rgcms.html#_rgcms_classes_super_ViewModel"><code>ViewModel</code></a> interface. The memento is as defined by the
interface’s methods: the programmer has full control (but also full responsibility) for the string memento.</p>
</div></div></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>DTO</p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@XmlRootElement</span>(<span class="string"><span class="delimiter">"</span><span class="content">customer</span><span class="delimiter">"</span></span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">CustomerDto</span> { ... }</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>Annotate using JAXB <a href="rgant.html#<em>rgant-XmlRootElement"><code>@XmlRootElement</code></a> annotation. Memento
derived automatically by serializing the XML graph as implied by the JAXB annotations. Note that (unlike <code>@ViewModel</code>
et al) this state _can</em> include collections.</p>
</div></div></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>JAXB-annotated DTOs are discussed in more detail immediately <a href="rg.html#_ugbtb_view-models_jaxb">below</a>.</p>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_view-models_jaxb">2.3. JAXB-annotated DTOs</h3>
<div class="paragraph">
<p>As noted in the <a href="#_ugbtb_view-models_use-cases">introduction</a>, view models can also be defined using
JAXB annotations. The serialized form of these view models is therefore XML, which also enables these view models
to act as DTOs.</p>
</div>
<div class="paragraph">
<p>In case it’s not obvious, these DTOs are still usable as "regular" view models; they will render in the
<a href="ugvw.html">Wicket viewer</a> just like any other. In fact, these JAXB-annotated view models are in many regards the
most powerful of all the various ways of writing view models:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>their entire state (collections as well as properties) is automatically managed from interaction to interaction.<br></p>
<div class="paragraph">
<p>In contrast, using <a href="rgant.html#_rgant-ViewModel"><code>@ViewModel</code></a> (or its
<a href="rgant.html#_rgant-DomainObject_nature"><code>@DomainObject#nature()</code></a> equivalent) will only manage the state of
properties, but not collections. And if using the <a href="rgcms.html#_rgcms_classes_super_ViewModel"><code>ViewModel</code></a>
interface, then the programmer must write all the state management (lots of boilerplate).</p>
</div>
</li>
<li>
<p>JAXB-annotated view models are editable.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The examples in this section uses the DTO for <code>ToDoItem</code>, taken from the (non-ASF) <a href="http://github.com/isisaddons/isis-app-todoapp">Isis addons' todoapp</a>. This DTO is defined as follows:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">package</span> <span class="namespace">todoapp.app.viewmodels.todoitem.v1</span>; <i class="conum" data-value="1"></i><b>(1)</b>
<span class="annotation">@XmlRootElement</span>(name = <span class="string"><span class="delimiter">"</span><span class="content">toDoItemDto</span><span class="delimiter">"</span></span>) <i class="conum" data-value="2"></i><b>(2)</b>
<span class="annotation">@XmlType</span>(
propOrder = { <i class="conum" data-value="3"></i><b>(3)</b>
<span class="string"><span class="delimiter">"</span><span class="content">majorVersion</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">minorVersion</span><span class="delimiter">"</span></span>,
<span class="string"><span class="delimiter">"</span><span class="content">description</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">category</span><span class="delimiter">"</span></span>, ...
<span class="string"><span class="delimiter">"</span><span class="content">toDoItem</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">similarItems</span><span class="delimiter">"</span></span>
}
)
<span class="annotation">@DomainObjectLayout</span>(
titleUiEvent = TitleUiEvent.Doop.class <i class="conum" data-value="4"></i><b>(4)</b>
)
<span class="directive">public</span> <span class="type">class</span> <span class="class">ToDoItemV1_1</span> <span class="directive">implements</span> Dto { <i class="conum" data-value="5"></i><b>(5)</b>
<span class="annotation">@XmlElement</span>(required = <span class="predefined-constant">true</span>, defaultValue = <span class="string"><span class="delimiter">"</span><span class="content">1</span><span class="delimiter">"</span></span>) <i class="conum" data-value="6"></i><b>(6)</b>
<span class="directive">public</span> <span class="directive">final</span> <span class="predefined-type">String</span> getMajorVersion() { <span class="keyword">return</span> <span class="string"><span class="delimiter">"</span><span class="content">1</span><span class="delimiter">"</span></span>; }
<span class="annotation">@XmlElement</span>(required = <span class="predefined-constant">true</span>, defaultValue = <span class="string"><span class="delimiter">"</span><span class="content">1</span><span class="delimiter">"</span></span>) <i class="conum" data-value="7"></i><b>(7)</b>
<span class="directive">public</span> <span class="predefined-type">String</span> getMinorVersion() { <span class="keyword">return</span> <span class="string"><span class="delimiter">"</span><span class="content">1</span><span class="delimiter">"</span></span>; }
<span class="annotation">@XmlElement</span>(required = <span class="predefined-constant">true</span>) <i class="conum" data-value="8"></i><b>(8)</b>
<span class="annotation">@Getter</span> <span class="annotation">@Setter</span>
<span class="directive">protected</span> <span class="predefined-type">String</span> description;
<span class="annotation">@XmlElement</span>(required = <span class="predefined-constant">true</span>)
<span class="annotation">@Getter</span> <span class="annotation">@Setter</span>
<span class="directive">protected</span> <span class="predefined-type">String</span> category;
...
<span class="annotation">@Getter</span> <span class="annotation">@Setter</span> <i class="conum" data-value="9"></i><b>(9)</b>
<span class="directive">protected</span> ToDoItem toDoItem;
<span class="annotation">@XmlElementWrapper</span> <i class="conum" data-value="10"></i><b>(10)</b>
<span class="annotation">@XmlElement</span>(name = <span class="string"><span class="delimiter">"</span><span class="content">todoItem</span><span class="delimiter">"</span></span>)
<span class="annotation">@Getter</span> <span class="annotation">@Setter</span>
<span class="directive">protected</span> <span class="predefined-type">List</span><ToDoItem> similarItems = Lists.newArrayList();
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>package name encodes major version; see discussion on <a href="#_ugbtb_view-models_jaxb_versioning">versioning</a></td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>identifies this class as a view model and defines the root element for JAXB serialization</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>all properties in the class must be listed; (they can be ignored using <code>@XmlTransient</code>)</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>demonstrating use of UI events for a subscriber to provide the DTO’s title; see <a href="rgant.html#_rgant-DomainObjectLayout_titleUiEvent"><code>@DomainObjectLayout#titleUiEvent()</code></a>.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>class name encodes (major and) minor version; see discussion on <a href="#_ugbtb_view-models_jaxb_versioning">versioning</a></td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>again, see discussion on <a href="#_ugbtb_view-models_jaxb_versioning">versioning</a></td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>again, see discussion on <a href="#_ugbtb_view-models_jaxb_versioning">versioning</a></td>
</tr>
<tr>
<td><i class="conum" data-value="8"></i><b>8</b></td>
<td>simple scalar properties</td>
</tr>
<tr>
<td><i class="conum" data-value="9"></i><b>9</b></td>
<td>reference to a persistent entity; discussed <a href="#_ugbtb_view-models_jaxb_referencing-domain-entities">below</a></td>
</tr>
<tr>
<td><i class="conum" data-value="10"></i><b>10</b></td>
<td>reference to a collection of persistent entities; again discussed <a href="#_ugbtb_view-models_jaxb_referencing-domain-entities">below</a></td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_ugbtb_view-models_jaxb_referencing-domain-entities">2.3.1. Referencing Domain Entities</h4>
<div class="paragraph">
<p>It’s quite common for view models to be "backed by" (be projections of) some underlying domain entity. The
<code>ToDoItemDto</code> we’ve been using as the example in this section is an example: there is an underlying <code>ToDoItem</code> entity.</p>
</div>
<div class="paragraph">
<p>It wouldn’t make sense to serialize out the state of a persistent entity: the point of a DTO is to act as a facade
on top of the entity so that the implementation details (of the entity’s structure) don’t leak out to the consumer.
However, the identity of the underlying entity can be well defined; Apache Isis defines the
<a href="rgcms.html#_rgcms_schema-common">Common schema</a> which defines the <code><oid-dto></code> element (and corresponding <code>OidDto</code> class):
the object’s type and its identifier. This is basically a formal XML equivalent to the <code>Bookmark</code> object obtained
from the <a href="rgsvc.html#_rgsvc_api_BookmarkService"><code>BookmarkService</code></a>.</p>
</div>
<div class="paragraph">
<p>There is only one requirement to make this work: every referenced domain entity must be annotated with
<a href="rgant.html#_rgant-XmlJavaTypeAdapter"><code>@XmlJavaTypeAdapter</code></a>, specifying the framework-provided
<code>PersistentEntityAdapter.class</code>. This class is similar to the <code>BookmarkService</code>: it knows how to create an <code>OidDto</code>
from an object reference.</p>
</div>
<div class="paragraph">
<p>Thus, in our view model we can legitimately write:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">package</span> <span class="namespace">todoapp.app.viewmodels.todoitem.v1</span>;
...
public <span class="type">class</span> <span class="class">ToDoItemV1_1</span> <span class="directive">implements</span> Dto {
...
<span class="annotation">@Getter</span> <span class="annotation">@Setter</span>
<span class="directive">protected</span> ToDoItem toDoItem;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>All we need to do is remember to add that <code>@XmlJavaTypeAdapter</code> annotation to the referenced entity:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@XmlJavaTypeAdapter</span>(PersistentEntityAdapter.class)
<span class="directive">public</span> <span class="type">class</span> <span class="class">ToDoItem</span> ... {
...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>It’s also possible for a DTO to hold collections of objects. These can be of any type, either simple properties, or
references to other objects. The only bit of boilerplate that is required is the <code>@XmlElementWrapper</code> annotation. This instructs JAXB to create an XML element (based on the field name) to contain each of the elements. (If this is omitted then the contents of the collection are at the same level as the properties; almost certainly not what is required).</p>
</div>
<div class="paragraph">
<p>For example, the DTO also contains:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">package</span> <span class="namespace">todoapp.app.viewmodels.todoitem.v1</span>;
...
public <span class="type">class</span> <span class="class">ToDoItemV1_1</span> <span class="directive">implements</span> Dto {
...
<span class="annotation">@XmlElementWrapper</span>
<span class="annotation">@XmlElement</span>(name = <span class="string"><span class="delimiter">"</span><span class="content">todoItem</span><span class="delimiter">"</span></span>)
<span class="annotation">@Getter</span> <span class="annotation">@Setter</span>
<span class="directive">protected</span> <span class="predefined-type">List</span><ToDoItem> similarItems = Lists.newArrayList();
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>There’s nothing to prevent a JAXB DTO from containing rich graphs of data, parent containing children containing
children. Be aware though that all of this state will become the DTO’s memento, ultimately converted into a URL-safe
form, by way of the <a href="rgsvc.html#_rgsvc_spi_UrlEncodingService"><code>UrlEncodingService</code></a>.</p>
</div>
<div class="paragraph">
<p>There are limits to the lengths of URLs, however. Therefore the DTO should not include state that can easily be
derived from other information. If the URL does exceed limits, then provide a custom implementation of <a href="rgsvc.html#_rgsvc_spi_UrlEncodingService"><code>UrlEncodingService</code></a> to handle the memento string in some other fashion (eg substituting it with a GUID, with the memento cached somehow
on the server).</p>
</div>
</div>
<div class="sect3">
<h4 id="_ugbtb_view-models_jaxb_versioning">2.3.2. Versioning</h4>
<div class="paragraph">
<p>The whole point of using DTOs (in Apache Isis, at least) is to define a formal contact between two inter-operating but
independent applications. Since the only thing we can predicate about the future with any certainty is that it one or
both of these applications will change, we should version DTOs from the get-go. This allows us to make changes going forward without unnecessarily breaking existing consumers of the data.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>There are several ways that versioning might be accomplished; we base our guidelines on this <a href="http://www.xfront.com/Versioning.pdf">article</a> taken from Roger Costello’s blog, well worth a read.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>We can distinguish two types of changes:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>backwardly compatible changes</p>
</li>
<li>
<p>breaking changes.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>We can immediately say that the XSD namespace should change only when there is a major/breaking change, if following <a href="http://semver.org">semantic versioning</a> that means when we bump the major version number v1, v2, etc.</p>
</div>
<div class="paragraph">
<p>XML namespaces correspond (when using JAXB) to Java packages. We should therefore place our DTOs in a package that
contains only the major number; this package will eventually contain a range of DTOs that are intended to be backwardly compatible with one another. The package should also have a <code>package-info.java</code>; it is this that declares the XSD
namespace:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@javax</span>.xml.bind.annotation.XmlSchema(
namespace = <span class="string"><span class="delimiter">"</span><span class="content">http://viewmodels.app.todoapp/todoitem/v1/Dto.xsd</span><span class="delimiter">"</span></span>, <i class="conum" data-value="1"></i><b>(1)</b>
xmlns = {
<span class="annotation">@javax</span>.xml.bind.annotation.XmlNs(
namespaceURI = <span class="string"><span class="delimiter">"</span><span class="content">http://isis.apache.org/schema/common</span><span class="delimiter">"</span></span>,
prefix = <span class="string"><span class="delimiter">"</span><span class="content">com</span><span class="delimiter">"</span></span>
)
},
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED
)
<span class="keyword">package</span> <span class="namespace">todoapp.app.viewmodels.todoitem.v1</span>; <i class="conum" data-value="2"></i><b>(2)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>the namespace URI, used by the DTO residing in this package.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>the package in which the DTO resides. Note that this contains only the major version.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Although there is no requirement for the namespace URI to correspond to a physical URL, it should be unique. This usually means including a company domain name within the string.</p>
</div>
<div class="paragraph">
<p>As noted above, this package will contain multiple DTO classes all with the same namespace; these represent a set of
minor versions of the DTO, each subsequent one intended to be backwardly compatible with the previous. Since these DTO
classes will all be in the same package (as per the <a href="#_ugbtb_view-models_jaxb_using-packages-to-version">advice above</a>), the class should therefore include the minor version name:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">package</span> <span class="namespace">todoapp.app.viewmodels.todoitem.v1</span>; <i class="conum" data-value="1"></i><b>(1)</b>
...
public <span class="type">class</span> <span class="class">ToDoItemV1_1</span> <span class="directive">implements</span> Dto { <i class="conum" data-value="2"></i><b>(2)</b>
...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>package contains the major version only</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>DTO class contains the (major and) minor version</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>We also recommend that each DTO instance should also specify the version of the XSD schema that it is logically compatible
with. Probably most consumers will not persist the DTOs; they will be processed and then discarded. However, it would
be wrong to assume that is the case in all cases; some consumers might choose to persist the DTO (eg for replay at
some later state).</p>
</div>
<div class="paragraph">
<p>Thus:</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">ToDoItemV1_1</span> <span class="directive">implements</span> Dto {
<span class="annotation">@XmlElement</span>(required = <span class="predefined-constant">true</span>, defaultValue = <span class="string"><span class="delimiter">"</span><span class="content">1</span><span class="delimiter">"</span></span>)
<span class="directive">public</span> <span class="directive">final</span> <span class="predefined-type">String</span> getMajorVersion() { <span class="keyword">return</span> <span class="string"><span class="delimiter">"</span><span class="content">1</span><span class="delimiter">"</span></span>; } <i class="conum" data-value="1"></i><b>(1)</b>
<span class="annotation">@XmlElement</span>(required = <span class="predefined-constant">true</span>, defaultValue = <span class="string"><span class="delimiter">"</span><span class="content">1</span><span class="delimiter">"</span></span>)
<span class="directive">public</span> <span class="predefined-type">String</span> getMinorVersion() { <span class="keyword">return</span> <span class="string"><span class="delimiter">"</span><span class="content">1</span><span class="delimiter">"</span></span>; } <i class="conum" data-value="2"></i><b>(2)</b>
...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>returns the major version (in sync with the package)</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>returns the minor version (in sync with the class name)</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>These methods always return a hard-coded literal. Any instances serialized from these classes will implicitly "declare"
the (major and) minor version of the schema that they are compatible with. If a consumer has a minimum version that
it requires, it can therefore inspect the XML instance itself to determine if it is able to consume said XML.</p>
</div>
<div class="paragraph">
<p>If a new (minor) version of a DTO is required, then we recommend copying-and-pasting the previous version, eg:</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">ToDoItemV1_2</span> <span class="directive">implements</span> Dto {
<span class="annotation">@XmlElement</span>(required = <span class="predefined-constant">true</span>, defaultValue = <span class="string"><span class="delimiter">"</span><span class="content">1</span><span class="delimiter">"</span></span>)
<span class="directive">public</span> <span class="directive">final</span> <span class="predefined-type">String</span> getMajorVersion() { <span class="keyword">return</span> <span class="string"><span class="delimiter">"</span><span class="content">1</span><span class="delimiter">"</span></span>; }
<span class="annotation">@XmlElement</span>(required = <span class="predefined-constant">true</span>, defaultValue = <span class="string"><span class="delimiter">"</span><span class="content">2</span><span class="delimiter">"</span></span>)
<span class="directive">public</span> <span class="predefined-type">String</span> getMinorVersion() { <span class="keyword">return</span> <span class="string"><span class="delimiter">"</span><span class="content">2</span><span class="delimiter">"</span></span>; }
...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Obviously, only changes made must be backward compatible, eg new members must be optional.</p>
</div>
<div class="paragraph">
<p>Alternatively, you might also consider simply editing the source file, ie renaming the class and bumping up the value
returned by <code>getMinorVersion()</code>.</p>
</div>
<div class="paragraph">
<p>We also <em>don’t</em> recommend using inheritance (ie <code>ToDoItemV1_2</code> should not inherit from <code>ToDoItemV1_1</code>; this creates unnecessary complexity downstream if generating XSDs and DTOs for the downstream consumer.</p>
</div>
</div>
<div class="sect3">
<h4 id="_ugbtb_view-models_jaxb_generating-xsds-and-dtos">2.3.3. Generating XSDs and DTOs</h4>
<div class="paragraph">
<p>In the section <a href="#_ugbtb_view-models_jaxb_referencing-domain-entities">above</a> it was explained how a
view model DTO can transparent reference any "backing" entities; these references are converted to internal object
identifiers.</p>
</div>
<div class="paragraph">
<p>However, if the consumer of the XML is another Java process (eg running within an Apache Camel route), then you might
be tempted/expect to be able to use the same DTO within that Java process. After a little thought though you’ll realize
that (duh!) of course you cannot; the consumer runs in a different process space, and will not have references to
those containing entities.</p>
</div>
<div class="paragraph">
<p>There are therefore two options:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>either choose not to have the view model DTO reference any persistent entities, and simply limit the DTO to simple
scalars.<br></p>
<div class="paragraph">
<p>Such a DTO will then be usable in both the Apache Isis app (to generate the original XML) and in the consumer. The
<a href="rgsvc.html#_rgsvc_api_BookmarkService"><code>BookmarkService</code></a> can be used to obtain the object identifiers</p>
</div>
</li>
<li>
<p>alternatively, generate a different DTO for the consumer from the XSD of the view model DTO.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The (non-ASF) <a href="http://github.com/isisaddons/isis-app-todoapp">Isis addons' todoapp</a> uses the second approach; generating
the XSD and consumer’s DTO is mostly just boilerplate <code>pom.xml</code> file. In the todoapp this can be found in the <code>todoapp-xsd</code> Maven module, whose <code>pom.xml</code> is structured as two profiles:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><project</span> <span class="attribute-name">...</span> <span class="tag">></span>
<span class="tag"><artifactId></span>todoapp-xsd<span class="tag"></artifactId></span>
<span class="tag"><dependencies></span>
<span class="tag"><dependency></span>
<span class="tag"><groupId></span>${project.groupId}<span class="tag"></groupId></span>
<span class="tag"><artifactId></span>todoapp-app<span class="tag"></artifactId></span>
<span class="tag"></dependency></span>
<span class="tag"></dependencies></span>
<span class="tag"><profiles></span>
<span class="tag"><profile></span>
<span class="tag"><id></span>isis-xsd<span class="tag"></id></span>
<span class="tag"><activation></span>
<span class="tag"><property></span>
<span class="tag"><name></span>!skip.isis-xsd<span class="tag"></name></span>
<span class="tag"></property></span>
<span class="tag"></activation></span>
...
<span class="tag"></profile></span>
<span class="tag"><profile></span>
<span class="tag"><id></span>xjc<span class="tag"></id></span>
<span class="tag"><activation></span>
<span class="tag"><property></span>
<span class="tag"><name></span>!skip.xjc<span class="tag"></name></span>
<span class="tag"></property></span>
<span class="tag"></activation></span>
...
<span class="tag"></profile></span>
<span class="tag"></profiles></span>
<span class="tag"></project></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>isis-xsd</code> profile generates the XSD using the <a href="rgmvn.html#_rgmvn_xsd"><code>xsd</code> goal</a> of Isis' maven plugin:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><build></span>
<span class="tag"><plugins></span>
<span class="tag"><plugin></span>
<span class="tag"><groupId></span>org.apache.isis.tool<span class="tag"></groupId></span>
<span class="tag"><artifactId></span>isis-maven-plugin<span class="tag"></artifactId></span>
<span class="tag"><version></span>${isis.version}<span class="tag"></version></span>
<span class="tag"><configuration></span>
<span class="tag"><appManifest></span>todoapp.dom.ToDoAppDomManifest<span class="tag"></appManifest></span>
<span class="tag"><jaxbClasses></span>
<span class="tag"><jaxbClass></span>todoapp.app.viewmodels.todoitem.v1.ToDoItemV1_1<span class="tag"></jaxbClass></span>
<span class="tag"></jaxbClasses></span>
<span class="tag"><separate></span>false<span class="tag"></separate></span>
<span class="tag"><commonSchemas></span>false<span class="tag"></commonSchemas></span>
<span class="tag"></configuration></span>
<span class="tag"><dependencies></span>
<span class="tag"><dependency></span>
<span class="tag"><groupId></span>${project.groupId}<span class="tag"></groupId></span>
<span class="tag"><artifactId></span>todoapp-dom<span class="tag"></artifactId></span>
<span class="tag"><version></span>${project.version}<span class="tag"></version></span>
<span class="tag"></dependency></span>
<span class="tag"><dependency></span>
<span class="tag"><groupId></span>com.google.guava<span class="tag"></groupId></span>
<span class="tag"><artifactId></span>guava<span class="tag"></artifactId></span>
<span class="tag"><version></span>16.0.1<span class="tag"></version></span>
<span class="tag"></dependency></span>
<span class="tag"></dependencies></span>
<span class="tag"><executions></span>
<span class="tag"><execution></span>
<span class="tag"><phase></span>generate-sources<span class="tag"></phase></span>
<span class="tag"><goals></span>
<span class="tag"><goal></span>xsd<span class="tag"></goal></span>
<span class="tag"></goals></span>
<span class="tag"></execution></span>
<span class="tag"></executions></span>
<span class="tag"></plugin></span>
<span class="tag"><plugin></span>
<span class="tag"><artifactId></span>maven-assembly-plugin<span class="tag"></artifactId></span>
<span class="tag"><version></span>2.5.3<span class="tag"></version></span>
<span class="tag"><configuration></span>
<span class="tag"><descriptor></span>src/assembly/dep.xml<span class="tag"></descriptor></span>
<span class="tag"></configuration></span>
<span class="tag"><executions></span>
<span class="tag"><execution></span>
<span class="tag"><id></span>create-archive<span class="tag"></id></span>
<span class="tag"><phase></span>package<span class="tag"></phase></span>
<span class="tag"><goals></span>
<span class="tag"><goal></span>single<span class="tag"></goal></span>
<span class="tag"></goals></span>
<span class="tag"></execution></span>
<span class="tag"></executions></span>
<span class="tag"></plugin></span>
<span class="tag"></plugins></span>
<span class="tag"></build></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>todoapp.dom.ToDoAppDomManifest</code> is a cut-down version of the app manifest that identifies only the <code>dom</code> domain
services.</p>
</div>
<div class="paragraph">
<p>The <code>xjc</code> profile, meanwhile, uses the <code>maven-jaxb2-plugin</code> (a wrapper around the <code>schemagen</code> JDK tool) to generate a
DTO from the XSD generated by the preceding profile:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><build></span>
<span class="tag"><plugins></span>
<span class="tag"><plugin></span>
<span class="tag"><groupId></span>org.jvnet.jaxb2.maven2<span class="tag"></groupId></span>
<span class="tag"><artifactId></span>maven-jaxb2-plugin<span class="tag"></artifactId></span>
<span class="tag"><version></span>0.12.3<span class="tag"></version></span>
<span class="tag"><executions></span>
<span class="tag"><execution></span>
<span class="tag"><id></span>xjc-generate<span class="tag"></id></span>
<span class="tag"><phase></span>generate-sources<span class="tag"></phase></span>
<span class="tag"><goals></span>
<span class="tag"><goal></span>generate<span class="tag"></goal></span>
<span class="tag"></goals></span>
<span class="tag"></execution></span>
<span class="tag"></executions></span>
<span class="tag"><configuration></span>
<span class="tag"><removeOldOutput></span>true<span class="tag"></removeOldOutput></span>
<span class="tag"><schemaDirectory></span>
target/generated-resources/isis-xsd/viewmodels.app.todoapp
<span class="tag"></schemaDirectory></span>
<span class="tag"><schemaIncludes></span>
<span class="tag"><schemaInclude></span>todoitem/v1/Dto.xsd<span class="tag"></schemaInclude></span>
<span class="tag"></schemaIncludes></span>
<span class="tag"><bindingDirectory></span>src/main/resources<span class="tag"></bindingDirectory></span>
<span class="tag"><bindingIncludes></span>
<span class="tag"><bindingInclude></span>binding.xml<span class="tag"></bindingInclude></span>
<span class="tag"></bindingIncludes></span>
<span class="tag"><catalog></span>src/main/resources/catalog.xml<span class="tag"></catalog></span>
<span class="tag"></configuration></span>
<span class="tag"></plugin></span>
<span class="tag"><plugin></span>
<span class="tag"><groupId></span>org.codehaus.mojo<span class="tag"></groupId></span>
<span class="tag"><artifactId></span>build-helper-maven-plugin<span class="tag"></artifactId></span>
<span class="tag"><version></span>1.9.1<span class="tag"></version></span>
<span class="tag"><executions></span>
<span class="tag"><execution></span>
<span class="tag"><id></span>add-source<span class="tag"></id></span>
<span class="tag"><phase></span>generate-sources<span class="tag"></phase></span>
<span class="tag"><goals></span>
<span class="tag"><goal></span>add-source<span class="tag"></goal></span>
<span class="tag"></goals></span>
<span class="tag"><configuration></span>
<span class="tag"><sources></span>
<span class="tag"><source></span>target/generated-sources/xjc<span class="tag"></source></span>
<span class="tag"></sources></span>
<span class="tag"></configuration></span>
<span class="tag"></execution></span>
<span class="tag"></executions></span>
<span class="tag"></plugin></span>
<span class="tag"></plugins></span>
<span class="tag"></build></span></code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_ugbtb_decoupling">3. Decoupling</h2>
<div class="sectionbody">
<div class="paragraph">
<p>We use Java packages as a way to group related domain objects together; the package name forms a namespace. We can then
reason about all the classes in that package/namespace as a single unit, or module.</p>
</div>
<div class="paragraph">
<p>This section describes how to use Apache Isis' features to ensure that your domain application remains decoupled. The techniques described here are also the ones that have been adopted by the various <a href="http://github.com/isisaddons">Isis Addons</a> modules (not ASF) for security, commands, auditing etc.</p>
</div>
<div class="paragraph">
<p>The following sections describe how to re-assemble an application, in particular where some modules are in-house but others are potentially third-party (eg the Isis Addons modules).</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>There is some overlap with OSGi and Java 9’s Jigsaw concepts of "module"; in the future we expect to refactor Apache Isis to leverage these module systems.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect2">
<h3 id="_ugbtb_decoupling_db-schemas">3.1. Database Schemas</h3>
<div class="paragraph">
<p>In the same way that Java packages act as a namespace for domain objects, it’s good practice to map domain entities to
their own (database) schemas. As of 1.9.0, all the <a href="http://www.isisaddons.org">Isis Addons</a> (non-ASF) modules do this, for example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@javax</span>.jdo.annotations.PersistenceCapable( ...
schema = <span class="string"><span class="delimiter">"</span><span class="content">isissecurity</span><span class="delimiter">"</span></span>,
table = <span class="string"><span class="delimiter">"</span><span class="content">ApplicationUser</span><span class="delimiter">"</span></span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">ApplicationUser</span> ... { ... }</code></pre>
</div>
</div>
<div class="paragraph">
<p>results in a <code>CREATE TABLE</code> statement of:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="sql"><span class="class">CREATE</span> <span class="type">TABLE</span> isissecurity.<span class="string"><span class="delimiter">"</span><span class="content">ApplicationUser</span><span class="delimiter">"</span></span> (
...
)</code></pre>
</div>
</div>
<div class="paragraph">
<p>while:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@javax</span>.jdo.annotations.PersistenceCapable( ...
schema = <span class="string"><span class="delimiter">"</span><span class="content">isisaudit</span><span class="delimiter">"</span></span>,
table=<span class="string"><span class="delimiter">"</span><span class="content">AuditEntry</span><span class="delimiter">"</span></span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">AuditEntry</span> ... { ... }</code></pre>
</div>
</div>
<div class="paragraph">
<p>similarly results in:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="sql"><span class="class">CREATE</span> <span class="type">TABLE</span> isisaudit.<span class="string"><span class="delimiter">"</span><span class="content">AuditEntry</span><span class="delimiter">"</span></span> (
...
)</code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>If for some reason you don’t want to use schemas (though we strongly recommend that you do), then note that you can override the <code>@PersistenceCapable</code> annotation by providing XML metadata (the <code>mappings.jdo</code> file); see the section on <a href="rgcfg.html#_rgcfg_configuring-datanucleus">configuring DataNucleus Overriding Annotations</a> for more details.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_listener_to_create_schema">3.1.1. Listener to create schema</h4>
<div class="paragraph">
<p>JDO/DataNucleus does not automatically create these schema objects, but it <em>does</em> provide a listener callback API
on the initialization of each class into the JDO metamodel.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Actually, the above statement isn’t quite true. In DN 3.2.x (as used by Apache Isis up to v1.8.0) there was no support for schemas. As of Apache Isis 1.9.0 and DN 4.0 there is now support. But we implemented this feature initially against DN 3.2.x, and it still works, so for now we’ve decided to leave it in.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Therefore Apache Isis attaches a listener, <code>CreateSchemaObjectFromClassMetadata</code>, that checks for the schema’s existence, and creates the schema if required.</p>
</div>
<div class="paragraph">
<p>The guts of its implementation is:</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">CreateSchemaObjectFromClassMetadata</span>
<span class="directive">implements</span> MetaDataListener,
DataNucleusPropertiesAware {
<span class="annotation">@Override</span>
<span class="directive">public</span> <span class="type">void</span> loaded(<span class="directive">final</span> AbstractClassMetaData cmd) { ... }
<span class="directive">protected</span> <span class="predefined-type">String</span> buildSqlToCheck(<span class="directive">final</span> AbstractClassMetaData cmd) {
<span class="directive">final</span> <span class="predefined-type">String</span> schemaName = schemaNameFor(cmd);
<span class="keyword">return</span> <span class="predefined-type">String</span>.format(
<span class="string"><span class="delimiter">"</span><span class="content">SELECT count(*) FROM INFORMATION_SCHEMA.SCHEMATA where SCHEMA_NAME = '%s'</span><span class="delimiter">"</span></span>, schemaName);
}
<span class="directive">protected</span> <span class="predefined-type">String</span> buildSqlToExec(<span class="directive">final</span> AbstractClassMetaData cmd) {
<span class="directive">final</span> <span class="predefined-type">String</span> schemaName = schemaNameFor(cmd);
<span class="keyword">return</span> <span class="predefined-type">String</span>.format(<span class="string"><span class="delimiter">"</span><span class="content">CREATE SCHEMA </span><span class="char">\"</span><span class="content">%s</span><span class="char">\"</span><span class="delimiter">"</span></span>, schemaName);
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>where <code>MetaDataListener</code> is the DataNucleus listener API:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">interface</span> <span class="class">MetaDataListener</span> {
<span class="type">void</span> loaded(AbstractClassMetaData cmd);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Although not formal API, the default <code>CreateSchemaObjectFromClassMetadata</code> has been designed to be easily overrideable if you
need to tweak it to support other RDBMS'. Any implementation must implement <code>org.datanucleus.metadata.MetaDataListener</code>:</p>
</div>
<div class="paragraph">
<p>The implementation provided has has been tested for HSQLDB, PostgreSQL and MS SQL Server, and is used automatically unless an alternative implementation is specified (as described in the section below).</p>
</div>
</div>
<div class="sect3">
<h4 id="_alternative_implementation">3.1.2. Alternative implementation</h4>
<div class="paragraph">
<p>An alternative implementation can be registered and used through the following configuration property:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="ini">isis.persistor.datanucleus.classMetadataLoadedListener=\
org.apache.isis.objectstore.jdo.datanucleus.CreateSchemaObjectFromClassMetadata</code></pre>
</div>
</div>
<div class="paragraph">
<p>Because this pertains to the JDO Objectstore we suggest you put this configuration property in <code>WEB-INF/persistor_datanucleus.properties</code>; but putting it in <code>isis.properties</code> will also work.</p>
</div>
<div class="paragraph">
<p>Any implementation must implement <code>org.datanucleus.metadata.MetaDataListener</code>. In many cases simply subclassing from <code>CreateSchemaObjectFromClassMetadata</code> and overriding <code>buildSqlToCheck(…​)</code> and <code>buildSqlToExec(…​)</code> should suffice.</p>
</div>
<div class="paragraph">
<p>If you <em>do</em> need more control, your implementation can also optionally implement <code>org.apache.isis.objectstore.jdo.datanucleus.DataNucleusPropertiesAware</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">interface</span> <span class="class">DataNucleusPropertiesAware</span> {
<span class="directive">public</span> <span class="type">void</span> setDataNucleusProperties(<span class="directive">final</span> <span class="predefined-type">Map</span><<span class="predefined-type">String</span>, <span class="predefined-type">String</span>> properties);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This provides access to the properties passed through to JDO/DataNucleus.</p>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<div class="paragraph">
<p>If you do extend Apache Isis' <code>CreateSchemaObjectFromClassMetadata</code> class for some other database, please <a href="https://issues.apache.org/jira/browse/ISIS">contribute back</a> your improvements.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_decoupling_mixins">3.2. Mixins</h3>
<div class="paragraph">
<p>A mixin object allows one class to contribute behaviour - actions, (derived) properties and
(derived) collections - to another domain object, either a domain entity or view model.</p>
</div>
<div class="paragraph">
<p>Some programming languages use the term "trait" instead of mixin, and some languages (such as AspectJ) define their own
syntax for defining such constructs. In Apache Isis a mixin is very similar to a domain service, however it also
defines a single 1-arg constructor that defines the type of the domain objects that it contributes to.</p>
</div>
<div class="paragraph">
<p>Why do this? The main reason is to allow the app to be decoupled, so that it doesn’t degrade into the proverbial
<a href="http://www.laputan.org/mud/mud.html#BigBallOfMud">"big ball of mud"</a>. Mixins (and contributions) allow dependency
to be inverted, so that the dependencies between modules can be kept acyclic and under control.</p>
</div>
<div class="sect3">
<h4 id="_example">3.2.1. Example</h4>
<div class="paragraph">
<p>This is probably best explained by way of an example. Suppose we have the <code>Customer</code> domain object which implements
a <code>DocumentHolder</code> interface:</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">Customer</span> <span class="directive">implements</span> DocumentHolder {
...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>We could then imagine a mixin that would contribute behaviour to list, add and remove the <code>Document</code>s for this holder:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Mixin</span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="directive">public</span> <span class="type">class</span> <span class="class">DocumentHolder_documents</span> {
<span class="directive">private</span> <span class="directive">final</span> DocumentHolder holder;
<span class="directive">public</span> DocumentHolder_documents(DocumentHolder holder) { <i class="conum" data-value="2"></i><b>(2)</b>
<span class="local-variable">this</span>.holder = holder;
}
<span class="annotation">@Action</span>(semantics=SemanticsOf.SAFE)
<span class="annotation">@ActionLayout</span>(contributed = Contributed.AS_ASSOCIATION)
<span class="annotation">@CollectionLayout</span>(render = RenderType.EAGERLY)
<span class="directive">public</span> <span class="predefined-type">List</span><<span class="predefined-type">Document</span>> documents() {
... <i class="conum" data-value="3"></i><b>(3)</b>
}
<span class="annotation">@Action</span>(semantics=SemanticsOf.IDEMPOTENT)
<span class="directive">public</span> DocumentHolder add(<span class="predefined-type">Document</span> document) {
... <i class="conum" data-value="4"></i><b>(4)</b>
}
<span class="annotation">@Action</span>(semantics=SemanticsOf.IDEMPOTENT)
<span class="directive">public</span> DocumentHolder remove(<span class="predefined-type">Document</span> document) {
... <i class="conum" data-value="5"></i><b>(5)</b>
}
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>alternatively can use <code>@DomainObject(nature=Nature.MIXIN)</code></td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>constructor indicates the type that is contributed to</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>implementation could, for example, use the (non-ASF)
<a href="http://github.com/isisaddons/isis-module-poly">Isis addons' poly</a> module.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>implementation would probably delegate to an injected repository to (ultimately) insert data into some table</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>implementation would probably delegate to an injected repository to (ultimately) delete data from some table</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The above example also omits any supporting methods, eg <code>hideXxx()</code>, <code>disableXxx()</code>, <code>validateXxx()</code>, etc.</p>
</div>
<div class="paragraph">
<p>In the user interface the "documents" collection, the "add" and the "delete" actions will appear to be part of
<code>Customer</code>. In essence the framework constructs a composite UI from the parts of multiple objects.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The (non-ASF) <a href="http://github.com/incodehq/incode-module-note">Incode note</a> and
<a href="http://github.com/incodehq/incode-module-commchannel">Incode commchannel</a> modules are real-world examples that use this
technique throughout.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_contributing_a_single_member">3.2.2. Contributing a single member</h4>
<div class="paragraph">
<p>The mixin can contribute as much or as little behaviour as makes sense. For example, in the example above it might be
that <code>Document</code>s are created through some other mechanism (eg scanned) and are never deleted. In that case, the
mixin might be simply:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Mixin</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">DocumentHolder_documents</span> {
..
public <span class="predefined-type">List</span><<span class="predefined-type">Document</span>> documents() { ... }
...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>For the case where there is only a single member being contributed, the special name "__" (that is, two underscores)
can be used instead. In this case, the contributee member’s name will be inferred from the mixin’s class name. If the
class name itself contains an underscore, then the last part of the class name will be used.</p>
</div>
<div class="paragraph">
<p>Thus, for a mixin whose class name is <code>DocumentHolder_documents</code>, the effective member name is <code>documents</code>. We could
therefore rewrite the mixin as:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Mixin</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">DocumentHolder_documents</span> {
..
public <span class="predefined-type">List</span><<span class="predefined-type">Document</span>> __() { ... }
...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The benefit of this is marginal unless there are supporting methods, in which case the removal of boilerplate is welcome:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Mixin</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">DocumentHolder_documents</span> {
..
public <span class="predefined-type">List</span><<span class="predefined-type">Document</span>> __() { ... }
<span class="directive">public</span> <span class="type">boolean</span> hide__() { ... }
<span class="directive">public</span> <span class="predefined-type">String</span> disable__() { ... }
...
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_programmatic_usage">3.2.3. Programmatic usage</h4>
<div class="paragraph">
<p>When a domain object is rendered, the framework will automatically instantiate all required mixins and delegate to them
dynamically. If writing integration tests or fixtures, or (sometimes) just regular domain logic, then you may need to
instantiate mixins directly.</p>
</div>
<div class="paragraph">
<p>For this you can use the
xref:rgsvc.adoc#_rgsvc_api_DomainObjectContainer_object-creation-api[<code>DomainObjectContainer#mixin(…​)</code>
method. For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java">DocumentHolder_documents mixin = container.mixin(DocumentHolder_documents.class, customer);</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <a href="ugtst.html#_ugtst_integ-test-support_bootstrapping_IntegrationTestAbstract"><code>IntegrationTestAbstract</code></a> and
<a href="rgcms.html#_rgcms_classes_super_FixtureScript"><code>FixtureScript</code></a> classes both provide a <code>mixin(…​)</code> convenience
method.</p>
</div>
</div>
<div class="sect3">
<h4 id="_other_reasons_to_use_mixins">3.2.4. Other reasons to use mixins</h4>
<div class="paragraph">
<p>In the introduction to this topic we mentioned that mixins are most useful for ensuring that the domain app remains
decoupled. This applies to the case where the contributee (eg <code>Customer</code>, being mixed into) is in one module, while
the contributor mixin (<code>DocumentHolder_documents</code>) is in some other module. The <code>customer</code> module knows about the
<code>document</code> module, but not vice versa.</p>
</div>
<div class="paragraph">
<p>However, you might also want to consider moving behaviour out of entities even within the same module, perhaps even
within the same Java package. And the reason for this is to support hot-reloading of Java classes, so that you can
modify and recompile your application without having to restart it. This can provide substantial productivity gains.</p>
</div>
<div class="paragraph">
<p>The Hotspot JVM has limited support for hot reloading; generally you can change method implementations but you cannot
introduce new methods. However, the <a href="https://dcevm.github.io/">DCEVM</a> open source project will patch the JVM to
support much more complete hot reloading support. There are also, of course, commercial products such as JRebel.</p>
</div>
<div class="paragraph">
<p>The main snag in all this is the DataNucleus enhancer…​ any change to entities is going to require the entity to be
re-enhanced, and the JDO metamodel recreated, which usually "stuffs things up". So hot-reloading of an app whose
fundamental structure is changing is likely to remain a no-no.</p>
</div>
<div class="paragraph">
<p>However, chances are that the structure of your domain objects (the data) will change much less rapidly than
the behaviour of those domain objects. Thus, it’s the behaviour that you’re most likely wanting to change while the
app is still running. If you move that behaviour out into mixins (or
<a href="#_ugbtb_decoupling_contributions">contributed services</a>), then these can be reloaded happily.
(When running in prototype mode), Apache Isis will automatically recreate the portion of the metamodel for any domain
object as it is rendered.</p>
</div>
</div>
<div class="sect3">
<h4 id="_related_reading">3.2.5. Related reading</h4>
<div class="paragraph">
<p>Mixins are an implementation of the <a href="http://www.artima.com/articles/dci_vision.html">DCI architecture</a> architecture, as formulated and described by <a href="https://en.wikipedia.org/wiki/Trygve_Reenskaug">Trygve Reenskaug</a> and <a href="https://en.wikipedia.org/wiki/Jim_Coplien">Jim Coplien</a>. Reenskaug was the inventor of the MVC pattern (and also the external
examiner for Richard Pawson’s PhD thesis), while Coplien has a long history in object-orientation, C++ and patterns.</p>
</div>
<div class="paragraph">
<p>DCI stands for Data-Context-Interaction and is presented as an evolution of object-oriented programming, but one where
behaviour is bound to objects dynamically rather than statically in some context or other. The <code>@Mixin</code>
pattern is Apache Isis' straightforward take on the same basic concept.</p>
</div>
<div class="paragraph">
<p>You might also wish to check out <a href="http://zest.apache.org">Apache Zest</a> (formerly Qi4J), which implements a much more
general purpose implementation of the same concepts.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_decoupling_contributions">3.3. Contributions</h3>
<div class="paragraph">
<p>Contributed services provide many of the same benefits as <a href="#_ugbtb_decoupling_mixins">mixins</a>;
indeed mixins are an evolution and refinement of the contributions concept.</p>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>It’s possible that contributions may be deprecated and eventually removed in a future version of the framework, to be replaced entirely by mixins.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The main difference between contributed services and mixins is that the actions of a contributed service will
contribute to <em>all</em> the parameters of its actions, whereas a mixin only contributes to the type accepted in its
constructor. Also, contributed services are long-lived
singletons, whereas mixins are instantiated as required (by the framework) and then discarded almost immediately.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>There’s further useful information on contributed services in the reference guide, discussing the <a href="rgant.html#_rgant-DomainService_nature">@DomainService#nature()</a> attribute, for the <code>NatureOfService.VIEW_CONTRIBUTIONS_ONLY</code> nature.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_syntax">3.3.1. Syntax</h4>
<div class="paragraph">
<p>Any n-parameter action provided by a service will automatically be contributed to the list of actions for each of its (entity) parameters. From the viewpoint of the entity the action is called a contributed action.</p>
</div>
<div class="paragraph">
<p>For example, given a service:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">interface</span> <span class="class">Library</span> {
<span class="directive">public</span> Loan borrow(Loanable l, Borrower b);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>and the entities:</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">Book</span> <span class="directive">implements</span> Loanable { ... }</code></pre>
</div>
</div>
<div class="paragraph">
<p>and</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">LibraryMember</span> <span class="directive">implements</span> Borrower { ... }</code></pre>
</div>
</div>
<div class="paragraph">
<p>then the <code>borrow(…​)</code> action will be contributed to both <code>Book</code> and to <code>LibraryMember</code>.</p>
</div>
<div class="paragraph">
<p>This is an important capability because it helps to decouple the concrete classes from the services.</p>
</div>
<div class="paragraph">
<p>If necessary, though, this behaviour can be suppressed by annotating the service action with <code>@org.apache.isis.applib.annotations.NotContributed</code>.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">interface</span> <span class="class">Library</span> {
<span class="annotation">@NotContributed</span>
<span class="directive">public</span> Loan borrow(Loanable l, Borrower b);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>If annotated at the interface level, then the annotation will be inherited by every concrete class. Alternatively the annotation can be applied at the implementation class level and only apply to that particular implementation.</p>
</div>
<div class="paragraph">
<p>Note that an action annotated as being <code>@NotContributed</code> will still appear in the service menu for the service. If an action should neither be contributed nor appear in service menu items, then simply annotate it as <code>@Hidden</code>.</p>
</div>
</div>
<div class="sect3">
<h4 id="_contributed_action">3.3.2. Contributed Action</h4>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
TODO
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_contributed_property">3.3.3. Contributed Property</h4>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
TODO
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_contributed_collection">3.3.4. Contributed Collection</h4>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
TODO
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_decoupling_vetoing-visibility">3.4. Vetoing Visibility</h3>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
TODO - a write-up of the "vetoing subscriber" design pattern, eg as described in the <a href="rgsvc.html#_rgsvc_api_BookmarkService"><code>BookmarkService</code></a>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>eg if included an addon such as auditing or security.</p>
</div>
<div class="paragraph">
<p>solution is to write a domain event subscriber that vetoes the visibility</p>
</div>
<div class="paragraph">
<p>All the addons actions inherit from common base classes so this can be as broad-brush or fine-grained as required</p>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_decoupling_event-bus">3.5. Event Bus</h3>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
TODO - see <a href="rgsvc.html#_rgsvc_api_EventBusService"><code>EventBusService</code></a>, <a href="rgant.html#_rgant-Action_domainEvent"><code>@Action#domainEvent()</code></a>, <a href="rgant.html#_rgant-Property_domainEvent"><code>@Property#domainEvent()</code></a>, <a href="rgant.html#_rgant-Collection_domainEvent"><code>@Collection#domainEvent()</code></a>, <a href="rgsvc.html#_rgsvc_api_WrapperFactory"><code>WrapperFactory</code></a>.
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_decoupling_pushing-changes">3.6. Pushing Changes</h3>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>This technique is much less powerful than using <a href="#_ugbtb_decoupling_event-bus">the event bus</a> . We present it mostly for completeness.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_when_a_property_is_changed">3.6.1. When a property is changed</h4>
<div class="paragraph">
<p>If you want to invoke functionality whenever a property is changed by the user, then you should create a supporting <code>modifyXxx()</code> method and include the functionality within that. The syntax is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">void</span> modifyPropertyName(PropertyType param) { ... }</code></pre>
</div>
</div>
<div class="paragraph">
<p>Why not just put this functionality in the setter? Well, the setter is used by the object store to recreate the state of an already persisted object. Putting additional behaviour in the setter would cause it to be triggered incorrectly.</p>
</div>
<div class="paragraph">
<p>For example:</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">Order</span>() {
<span class="directive">public</span> <span class="predefined-type">Integer</span> getAmount() { ... }
<span class="directive">public</span> <span class="type">void</span> setAmount(<span class="predefined-type">Integer</span> amount) { ... }
<span class="directive">public</span> <span class="type">void</span> modifyAmount(<span class="predefined-type">Integer</span> amount) { <i class="conum" data-value="1"></i><b>(1)</b>
setAmount(amount); <i class="conum" data-value="3"></i><b>(3)</b>
addToTotal(amount); <i class="conum" data-value="2"></i><b>(2)</b>
}
...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The <code>modifyAmount()</code> method calls …​</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>…​ the <code>addToTotal()</code> (not shown) to maintain some running total.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>We don’t want this <code>addToCall()</code> method to be called when pulling the object back from the object store, so we put it into the modify, not the setter.</p>
</div>
<div class="paragraph">
<p>You may optionally also specify a <code>clearXxx()</code> which works the same way as modify <code>modify Xxx()</code> but is called when the property is cleared by the user (i.e. the current value replaced by nothing). The syntax is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">void</span> clearPropertyName() { ... }</code></pre>
</div>
</div>
<div class="paragraph">
<p>To extend the above example:</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">Order</span>() {
<span class="directive">public</span> <span class="predefined-type">Integer</span> getAmount() { ... }
<span class="directive">public</span> <span class="type">void</span> setAmount(<span class="predefined-type">Integer</span> amount) { ... }
<span class="directive">public</span> <span class="type">void</span> modifyAmount(<span class="predefined-type">Integer</span> amount) { ... }
<span class="directive">public</span> <span class="type">void</span> clearAmount() {
removeFromTotal(<span class="local-variable">this</span>.amount);
setAmount(<span class="predefined-constant">null</span>);
}
...
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_when_a_collection_is_modified">3.6.2. When a collection is modified</h4>
<div class="paragraph">
<p>A collection may have a corresponding <code>addToXxx()</code> and/or
<code>removeFromXxx()</code> method. If present, and direct manipulation of the
contents of the connection has not been disabled (see ?), then they will
be called (instead of adding/removing an object directly to the
collection returned by the accessor).</p>
</div>
<div class="paragraph">
<p>The reason for this behaviour is to allow other behaviour to be
triggered when the contents of the collection is altered. That is, it is
directly equivalent to the supporting <code>modifyXxx()</code> and <code>clearXxx()</code>
methods for properties (see ?).</p>
</div>
<div class="paragraph">
<p>The syntax is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">void</span> addTo<CollectionName>(EntityType param) { ... }</code></pre>
</div>
</div>
<div class="paragraph">
<p>and</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">void</span> removeFromCollectionName(EntityType param) { ... }</code></pre>
</div>
</div>
<div class="paragraph">
<p>where <code>EntityType</code> is the same type as the generic collection type.</p>
</div>
<div class="paragraph">
<p>For example:</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">Employee</span> { ... }
<span class="directive">public</span> <span class="type">class</span> <span class="class">Department</span> {
<span class="directive">private</span> <span class="type">int</span> numMaleEmployees; <i class="conum" data-value="1"></i><b>(1)</b>
<span class="directive">private</span> <span class="type">int</span> numFemaleEmployees; <i class="conum" data-value="2"></i><b>(2)</b>
<span class="directive">private</span> <span class="predefined-type">Set</span><Employee> employees = <span class="keyword">new</span> <span class="predefined-type">TreeSet</span><Employee>();
<span class="directive">public</span> <span class="predefined-type">Set</span><Employee> getEmployees() {
<span class="keyword">return</span> employees;
}
<span class="directive">private</span> <span class="type">void</span> setEmployees(<span class="predefined-type">Set</span><Employee> employees) {
<span class="local-variable">this</span>.employees = employees;
}
<span class="directive">public</span> <span class="type">void</span> addToEmployees(Employee employee) { <i class="conum" data-value="3"></i><b>(3)</b>
numMaleEmployees += countOneMale(employee);
numFemaleEmployees += countOneFemale(employee);
employees.add(employee);
}
<span class="directive">public</span> <span class="type">void</span> removeFromEmployees(Employee employee) { <i class="conum" data-value="4"></i><b>(4)</b>
numMaleEmployees -= countOneMale(employee);
numFemaleEmployees -= countOneFemale(employee);
employees.remove(employee);
}
<span class="directive">private</span> <span class="type">int</span> countOneMale(Employee employee) { <span class="keyword">return</span> employee.isMale()?<span class="integer">1</span>:<span class="integer">0</span>; }
<span class="directive">private</span> <span class="type">int</span> countOneFemale(Employee employee) { <span class="keyword">return</span> employee.isFemale()?<span class="integer">1</span>:<span class="integer">0</span>; }
...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>maintain a count of the number of male …​</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>…​ and female employees (getters and setters omitted)</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>the <a href="rgcms.html#_rgcms_methods_prefixes_addTo"><code>addTo…​()</code></a> method increments the derived properties</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>the <a href="rgcms.html#_rgcms_methods_prefixes_removeFrom"><code>removeFrom…​()</code></a> method similarly decrements the derived properties</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_ugbtb_i18n">4. i18n</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Apache Isis' support for internationlization (i18n) allows every element of the domain model (the class names, property names, action names, parameter names and so forth) to be translated.</p>
</div>
<div class="paragraph">
<p>It also supports translations of messages raised imperatively, by which we mean as the result of a call to <code>title()</code> to obtain an object’s title, or messages resulting from any business rule violations (eg <a href="rgcms.html#_rgcms_methods_prefixes_disable"><code>disable…​()</code></a> or <a href="rgcms.html#_rgcms_methods_prefixes_validate"><code>validate…​()</code></a>, and so on.</p>
</div>
<div class="paragraph">
<p>The <a href="ugvw.html">Wicket viewer</a> (that is, its labels and messages) is also internationalized using the same mechanism. If no translations are available, then the Wicket viewer falls back to using Wicket resource bundles.</p>
</div>
<div class="paragraph">
<p>Isis does not translate the values of your domain objects, though. So, if you have a domain concept such as <code>Country</code> whose name is intended to be localized according to the current user, you will need to model this yourself.</p>
</div>
<div class="sect2">
<h3 id="_ugbtb_i18n_implementation-approach">4.1. Implementation Approach</h3>
<div class="paragraph">
<p>Most Java frameworks tackle i18n by using Java’s own <code>ResourceBundle</code> API. However, there are some serious drawbacks in this approach, including:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>if a string appears more than once (eg "name" or "description") then it must be translated everywhere it appears in every resource bundle file</p>
</li>
<li>
<p>there is no support for plural forms (see this <a href="http://stackoverflow.com/questions/14326653/java-internationalization-i18n-with-proper-plurals/14327683#14327683">SO answer</a>)</p>
</li>
<li>
<p>there is no tooling support for translators</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Apache Isis therefore takes a different approach, drawing inspiration from GNU’s <a href="https://www.gnu.org/software/gettext/manual/index.html">gettext</a> API and specifically its <code>.pot</code> and <code>.po</code> files. These are intended to be used as follows:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>the <code>.pot</code> (portable object template) file holds the message text to be translated</p>
</li>
<li>
<p>this file is translated into multiple <code>.po</code> (portable object) files, one per supported locale</p>
</li>
<li>
<p>these <code>.po</code> files are renamed according to their locale, and placed into the 'appropriate' location to be picked up by the runtime. The name of each <code>.po</code> resolved in a very similar way to resource bundles.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The format of the <code>.pot</code> and <code>.po</code> files is identical; the only difference is that the <code>.po</code> file has translations for each of the message strings. These message strings can also have singular and plural forms.</p>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Although Apache Isis' implementation is modelled after GNU’s API, it does <em>not</em> use any GNU software. This is for two reasons: (a) to simplify the toolchain/developer experience, and (b) because GNU software is usually GPL, which would be incompatible with the Apache license.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>This design tackles all the issues of <code>ResourceBundle</code>s:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>the <code>.po</code> message format is such that any given message text to translate need only be translated once, even if it appears in multiple places in the application (eg "Name")</p>
</li>
<li>
<p>the <code>.po</code> message format includes translations for (optional) plural form as well as singular form</p>
</li>
<li>
<p>there are lots of freely available editors <a href="https://www.google.co.uk/search?q=.po+file+editor">to be found</a>, many summarized on this <a href="https://www.drupal.org/node/11131">Drupal.org</a> webpage.<br></p>
<div class="paragraph">
<p>In fact, there are also online communities/platforms of translators to assist with translating files. One such is <a href="https://crowdin.com/">crowdin</a> (nb: this link does not imply endorsement).</p>
</div>
</li>
</ul>
</div>
<div class="paragraph">
<p>In Apache Isis' implementation, if the translation is missing from the <code>.po</code> file then the original message text from the <code>.pot</code> file will be returned. In fact, it isn’t even necessary for there to be any <code>.po</code> files; <code>.po</code> translations can be added piecemeal as the need arises.</p>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_i18n_translation-service">4.2. <code>TranslationService</code></h3>
<div class="paragraph">
<p>The cornerstone of Apache Isis' support for i18n is the <code>TranslationService</code> service. This is defined in the applib with the following API:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">interface</span> <span class="class">TranslationService</span> {
<span class="directive">public</span> <span class="predefined-type">String</span> translate( <i class="conum" data-value="1"></i><b>(1)</b>
<span class="directive">final</span> <span class="predefined-type">String</span> context,
<span class="directive">final</span> <span class="predefined-type">String</span> text);
<span class="directive">public</span> <span class="predefined-type">String</span> translate( <i class="conum" data-value="2"></i><b>(2)</b>
<span class="directive">final</span> <span class="predefined-type">String</span> context,
<span class="directive">final</span> <span class="predefined-type">String</span> singularText,
<span class="directive">final</span> <span class="predefined-type">String</span> pluralText,
<span class="directive">final</span> <span class="type">int</span> num);
<span class="directive">public</span> <span class="type">enum</span> Mode {
READ,
WRITE;
}
Mode getMode(); <i class="conum" data-value="3"></i><b>(3)</b>
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>is to translate the singular form of the text</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>is to translate the plural form of the text</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>indicates whether the translation service is in read or write mode.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The <code>translate(…​)</code> methods are closely modelled on GNU’s gettext API. The first version is used when no translation is required, the second is when both a singular and plural form will be required, with the <code>num</code> parameter being used to select which is returned. In both cases the <code>context</code> parameter provides some contextual information for the translator; this generally corresponds to the class member.</p>
</div>
<div class="paragraph">
<p>The mode meanwhile determines the behaviour of the service. More on this below.</p>
</div>
<div class="sect3">
<h4 id="__code_translationservicepo_code">4.2.1. <code>TranslationServicePo</code></h4>
<div class="paragraph">
<p>Isis provides a default implementation of <code>TranslationService</code>, namely <code>TranslationServicePo</code>.</p>
</div>
<div class="paragraph">
<p>If the service is running in the normal read mode, then it simply provides translations for the locale of the current user. This means locates the appropriate <code>.po</code> file (based on the requesting user’s locale), finds the translation and returns it.</p>
</div>
<div class="paragraph">
<p>If however the service is configured to run in write mode, then it instead records the fact that the message was requested to be translated (a little like a spy/mock in unit testing mock), and returns the original message. The service can then be queried to discover which messages need to be translated. All requested translations are written into the <code>.pot</code> file.</p>
</div>
<div class="paragraph">
<p>To make the service as convenient as possible to use, the service configures itself as follows:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>if running in prototype mode <a href="rgcfg.html#_rgcfg_deployment-types">deployment type</a> or during integration tests, then the service runs in <strong>write</strong> mode, in which case it records all translations into the <code>.pot</code> file. The <code>.pot</code> file is written out when the system is shutdown.</p>
</li>
<li>
<p>if running in server (production) mode <a href="rgcfg.html#_rgcfg_deployment-types">deployment type</a>, then the service runs in <strong>read</strong> mode. It is also possible to set a configuration setting in <code>isis.properties</code> to force read mode even if running in prototype mode (useful to manually test/demo the translations).</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>When running in write mode the original text is returned to the caller untranslated. If in read mode, then the translated <code>.po</code> files are read and translations provided as required.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_i18n_imperative-messages">4.3. Imperative messages</h3>
<div class="paragraph">
<p>The <code>TranslationService</code> is used internally by Apache Isis when building up the metamodel; the name and description of every class, property, collection, action and action parameter is automatically translated. Thus the simple act of bootstrapping Apache Isis will cause most of the messages requiring translation (that is: those for the Apache Isis metamodel) to be captured by the <code>TranslationService</code>.</p>
</div>
<div class="paragraph">
<p>However, for an application to be fully internationalized, any validation messages (from either <code>disableXxx()</code> or <code>validateXxx()</code> supporting methods) and also possibly an object’s title (from the <code>title()</code> method) will also require translation. Moreover, these messages must be captured in the <code>.pot</code> file such that they can be translated.</p>
</div>
<div class="sect3">
<h4 id="__code_translatablestring_code">4.3.1. <code>TranslatableString</code></h4>
<div class="paragraph">
<p>The first part of the puzzle is tackled by an extension to Apache Isis' programming model. Whereas previously the <code>disableXxx()</code> / <code>validateXxx()</code> / <code>title()</code> methods could only return a <code>java.lang.String</code>, they may now optionally return a <code>TranslatableString</code> (defined in Isis applib) instead.</p>
</div>
<div class="paragraph">
<p>Here’s a (silly) example from the <a href="ugfun.html#_ugfun_getting-started_simpleapp-archetype">SimpleApp archetype</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> TranslatableString validateUpdateName(<span class="directive">final</span> <span class="predefined-type">String</span> name) {
<span class="keyword">return</span> name.contains(<span class="string"><span class="delimiter">"</span><span class="content">!</span><span class="delimiter">"</span></span>)? TranslatableString.tr(<span class="string"><span class="delimiter">"</span><span class="content">Exclamation mark is not allowed</span><span class="delimiter">"</span></span>): <span class="predefined-constant">null</span>;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This corresponds to the following entry in the <code>.pot</code> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="ini">#: dom.simple.SimpleObject#updateName()
msgid "Exclamation mark is not allowed"
msgstr ""</code></pre>
</div>
</div>
<div class="paragraph">
<p>The full API of <code>TranslatableString</code> is modelled on the design of GNU gettext (in particular the <a href="https://code.google.com/p/gettext-commons/wiki/Tutorial">gettext-commons</a> library):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="directive">final</span> <span class="type">class</span> <span class="class">TranslatableString</span> {
<span class="directive">public</span> <span class="directive">static</span> TranslatableString tr( <i class="conum" data-value="1"></i><b>(1)</b>
<span class="directive">final</span> <span class="predefined-type">String</span> pattern,
<span class="directive">final</span> <span class="predefined-type">Object</span>... paramArgs) { ... }
<span class="directive">public</span> <span class="directive">static</span> TranslatableString trn( <i class="conum" data-value="2"></i><b>(2)</b>
<span class="directive">final</span> <span class="predefined-type">String</span> singularPattern,
<span class="directive">final</span> <span class="predefined-type">String</span> pluralPattern,
<span class="directive">final</span> <span class="type">int</span> number,
<span class="directive">final</span> <span class="predefined-type">Object</span>... paramArgs) { ... }
<span class="directive">public</span> <span class="predefined-type">String</span> translate( <i class="conum" data-value="3"></i><b>(3)</b>
<span class="directive">final</span> TranslationService translationService,
<span class="directive">final</span> <span class="predefined-type">String</span> context) { ... }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>returns a translatable string with a single pattern for both singular and plural forms.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>returns a translatable string with different patterns for singular and plural forms; the one to use is determined by the 'number' argument</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>translates the string using the provided <code>TranslationService</code>, using the appropriate singular/regular or plural form, and interpolating any arguments.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The interpolation uses the format <code>{xxx}</code>, where the placeholder can occur multiple times.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">final</span> TranslatableString ts = TranslatableString.tr(
<span class="string"><span class="delimiter">"</span><span class="content">My name is {lastName}, {firstName} {lastName}.</span><span class="delimiter">"</span></span>,
<span class="string"><span class="delimiter">"</span><span class="content">lastName</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">Bond</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">firstName</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">James</span><span class="delimiter">"</span></span>);</code></pre>
</div>
</div>
<div class="paragraph">
<p>would interpolate (for the English locale) as "My name is Bond, James Bond".</p>
</div>
<div class="paragraph">
<p>For a German user, on the other hand, if the translation in the corresponding <code>.po</code> file was:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="ini">#: xxx.yyy.Whatever#context()
msgid "My name is {lastName}, {firstName} {lastName}."
msgstr "Ich heisse {firstName} {lastName}."</code></pre>
</div>
</div>
<div class="paragraph">
<p>then the translation would be: "Ich heisse James Bond".</p>
</div>
<div class="paragraph">
<p>The same class is used in <a href="rgsvc.html#_rgsvc_api_DomainObjectContainer"><code>DomainObjectContainer</code></a> so that you can raise translatable info, warning and error messages; each of the relevant methods are overloaded.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">interface</span> <span class="class">DomainObjectContainer</span> {
<span class="type">void</span> informUser(<span class="predefined-type">String</span> message);
<span class="type">void</span> informUser(
TranslatableMessage message,
<span class="directive">final</span> <span class="predefined-type">Class</span><?> contextClass, <span class="directive">final</span> <span class="predefined-type">String</span> contextMethod); <i class="conum" data-value="1"></i><b>(1)</b>
...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>are concatenated together to form the context for the <code>.pot</code> file.</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="__code_translatableexception_code">4.3.2. <code>TranslatableException</code></h4>
<div class="paragraph">
<p>Another mechanism by which messages can be rendered to the user are as the result of exception messages thrown and recognized by an <a href="rgsvc.html#_rgsvc_spi_ExceptionRecognizer"><code>ExceptionRecognizer</code></a>.</p>
</div>
<div class="paragraph">
<p>In this case, if the exception implements <code>TranslatableException</code>, then the message will automatically be translated before being rendered. The <code>TranslatableException</code> itself takes the form:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">interface</span> <span class="class">TranslatableException</span> {
TranslatableString getTranslatableMessage(); <i class="conum" data-value="1"></i><b>(1)</b>
<span class="predefined-type">String</span> getTranslationContext(); <i class="conum" data-value="2"></i><b>(2)</b>
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>the message to translate. If returns <code>null</code>, then the <code>Exception#getMessage()</code> is used as a fallback</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>the context to use when translating the message</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_i18n_wicket-viewer">4.4. Wicket Viewer</h3>
<div class="paragraph">
<p>The <a href="ugvw.html">Wicket viewer</a> (its labels and messages) is also internationalized using
the <code>TranslationService</code>. This is done through an Isis-specific implementation of the Wicket framework’s <code>org.apache.wicket.Localizer</code> class, namely <code>LocalizerForIsis</code>.</p>
</div>
<div class="paragraph">
<p>The Wicket <code>Localizer</code> defines the following API:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="predefined-type">String</span> getString(
<span class="directive">final</span> <span class="predefined-type">String</span> key, <i class="conum" data-value="1"></i><b>(1)</b>
<span class="directive">final</span> <span class="predefined-type">Component</span> component, <i class="conum" data-value="2"></i><b>(2)</b>
<span class="directive">final</span> IModel<?> model,
<span class="directive">final</span> <span class="predefined-type">Locale</span> locale,
<span class="directive">final</span> <span class="predefined-type">String</span> style,
<span class="directive">final</span> <span class="predefined-type">String</span> defaultValue)
<span class="directive">throws</span> <span class="exception">MissingResourceException</span> { ... }</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The key to obtain the resource for</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>The component to get the resource for (if any)</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>For example, <code>key</code> might be a value such as "okLabel", while <code>component</code> an internal class of the Wicket viewer, such as <code>EntityPropertiesForm</code>.</p>
</div>
<div class="paragraph">
<p>The <code>LocalizerForIsis</code> implementation uses the <code>key</code> as the <code>msgId</code>, while the fully qualified class name of the
<code>component</code> is used as a context. There is one exception to this: if the component is the third-party select2
component (used for drop-downs), then that class name is used directly.</p>
</div>
<div class="paragraph">
<p>In the main, using Isis' i18n support means simply adding the appropriate translations to the <code>translation.po</code> file,
for each locale that you require. If the translations are missing then the original translations from the Wicket
resource bundles will be used instead.</p>
</div>
<div class="sect3">
<h4 id="_commonly_used">4.4.1. Commonly used</h4>
<div class="paragraph">
<p>Most of the translation requirements can be covered by adding in the following <code>msgId</code>s:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="properties">#: org.apache.isis.viewer.wicket.ui.pages.entity.EntityPage
msgid "CollectionContentsAsAjaxTablePanelFactory.Table"
msgstr "Table"
#: org.apache.isis.viewer.wicket.ui.pages.entity.EntityPage
msgid "CollectionContentsAsUnresolvedPanel.Hide"
msgstr "Hide"
#: org.apache.isis.viewer.wicket.ui.pages.entity.EntityPage
msgid "aboutLabel"
msgstr "About"
#: org.apache.isis.viewer.wicket.ui.pages.entity.EntityPage
msgid "cancelLabel"
msgstr "Cancel"
#: org.apache.isis.viewer.wicket.ui.pages.entity.EntityPage
msgid "datatable.no-records-found"
msgstr "No Records Found"
#: org.apache.isis.viewer.wicket.ui.pages.entity.EntityPage
msgid "editLabel"
msgstr "Edit"
#: org.wicketstuff.select2.Select2Choice
msgid "inputTooShortPlural"
msgstr "Please enter {number} more characters"
#: org.wicketstuff.select2.Select2Choice
msgid "inputTooShortSingular"
msgstr "Please enter 1 more character"
#: org.wicketstuff.select2.Select2Choice
msgid "loadMore"
msgstr "Load more"
#: org.apache.isis.viewer.wicket.ui.pages.entity.EntityPage
msgid "logoutLabel"
msgstr "Logout"
#: org.wicketstuff.select2.Select2Choice
msgid "noMatches"
msgstr "No matches"
#: org.apache.isis.viewer.wicket.ui.pages.entity.EntityPage
msgid "okLabel"
msgstr "OK"
#: org.wicketstuff.select2.Select2Choice
msgid "searching"
msgstr "Searching..."
#: org.wicketstuff.select2.Select2Choice
msgid "selectionTooBigPlural"
msgstr "You can only select {limit} items"
#: org.wicketstuff.select2.Select2Choice
msgid "selectionTooBigSingular"
msgstr "You can only select 1 item"</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_login_self_sign_up">4.4.2. Login/self-sign-up</h4>
<div class="paragraph">
<p>In addition, there are a reasonably large number of messages that are used for both login and the
<a href="ugvw.html#_ugvw_features_user-registration">user registration</a> (self sign-up) and password reset features.</p>
</div>
<div class="paragraph">
<p>These are:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="properties">#: org.apache.isis.viewer.wicket.ui.pages.login.WicketSignInPage
msgid "AutoLabel.CSS.required"
msgstr "Required"
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.signup.RegistrationFormPage
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.register.RegisterPage
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.password_reset.PasswordResetPage
msgid "confirmPasswordLabel"
msgstr "Confirm password"
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.signup.RegistrationFormPage
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.register.RegisterPage
msgid "emailIsNotAvailable"
msgstr "The given email is already in use"
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.password_reset.PasswordResetPage
msgid "emailPlaceholder"
msgstr "Enter your email"
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.signup.RegistrationFormPage
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.register.RegisterPage
msgid "emailPlaceholder"
msgstr "Enter an email for the new account"
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.signup.RegistrationFormPage
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.register.RegisterPage
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.password_reset.PasswordResetPage
msgid "emailLabel"
msgstr "Email"
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.signup.RegistrationFormPage
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.register.RegisterPage
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.password_reset.PasswordResetPage
msgid "emailSentMessage"
msgstr "An email has been sent to '${email}' for verification."
#: org.apache.isis.viewer.wicket.ui.pages.login.WicketSignInPage
msgid "forgotPasswordLinkLabel"
msgstr "Forgot your password?"
#: org.apache.isis.viewer.wicket.ui.pages.login.WicketSignInPage
msgid "loginHeader"
msgstr "Login"
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.password_reset.PasswordResetPage
msgid "noSuchUserByEmail"
msgstr "There is no account with this email"
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.password_reset.PasswordResetPage
msgid "noUserForAnEmailValidToken"
msgstr "The account seems to be either already deleted or has changed its email address. Please try again."
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.password_reset.PasswordResetPage
msgid "passwordChangeSuccessful"
msgstr "The password has been changed successfully. You can <a class=\"alert-success\" style=\"text-decoration:underline;\" href=\"${signInUrl}\">login</a> now."
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.password_reset.PasswordResetPage
msgid "passwordChangeUnsuccessful"
msgstr "There was a problem while updating the password. Please try again."
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.signup.RegistrationFormPage
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.register.RegisterPage
#: org.apache.isis.viewer.wicket.ui.pages.login.WicketSignInPage
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.password_reset.PasswordResetPage
msgid "passwordLabel"
msgstr "Password"
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.signup.RegistrationFormPage
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.register.RegisterPage
#: org.apache.isis.viewer.wicket.ui.pages.login.WicketSignInPage
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.password_reset.PasswordResetPage
msgid "passwordPlaceholder"
msgstr "Enter password"
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.password_reset.PasswordResetPage
msgid "passwordResetExpiredOrInvalidToken"
msgstr "You are trying to reset the password for an expired or invalid token"
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.password_reset.PasswordResetPage
msgid "passwordResetHeader"
msgstr "Forgot password"
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.password_reset.PasswordResetPage
msgid "passwordResetSubmitLabel"
msgstr "Submit"
#: org.apache.isis.viewer.wicket.ui.pages.login.WicketSignInPage
msgid "registerButtonLabel"
msgstr "Register"
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.register.RegisterPage
msgid "registerHeader"
msgstr "Register"
#: org.apache.isis.viewer.wicket.ui.pages.login.WicketSignInPage
msgid "rememberMeLabel"
msgstr "Remember Me"
#: org.apache.isis.viewer.wicket.ui.pages.login.WicketSignInPage
msgid "resetButtonLabel"
msgstr "Reset"
#: org.apache.isis.viewer.wicket.ui.pages.login.WicketSignInPage
msgid "signInButtonLabel"
msgstr "Sign in"
#: org.apache.isis.viewer.wicket.ui.pages.login.WicketSignInPage
msgid "signUpButtonLabel"
msgstr "Don't have an account? Sign up now."
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.signup.RegistrationFormPage
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.register.RegisterPage
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.password_reset.PasswordResetPage
msgid "signUpButtonLabel"
msgstr "Verify email"
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.signup.RegistrationFormPage
msgid "signUpHeader"
msgstr "Sign Up"
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.signup.RegistrationFormPage
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.register.RegisterPage
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.password_reset.PasswordResetPage
msgid "usernameIsNotAvailable"
msgstr "The provided username is already in use"
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.signup.RegistrationFormPage
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.register.RegisterPage
#: org.apache.isis.viewer.wicket.ui.pages.login.WicketSignInPage
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.password_reset.PasswordResetPage
msgid "usernameLabel"
msgstr "Username"
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.signup.RegistrationFormPage
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.register.RegisterPage
#: org.apache.isis.viewer.wicket.ui.pages.login.WicketSignInPage
#: org.apache.isis.viewer.wicket.ui.pages.accmngt.password_reset.PasswordResetPage
msgid "usernamePlaceholder"
msgstr "Username"</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_i18n_integration-testing">4.5. Integration Testing</h3>
<div class="paragraph">
<p>So much for the API; but as noted, it is also necessary to ensure that the required translations are recorded (by the <code>TranslationService</code>) into the <code>.pot</code> file.</p>
</div>
<div class="paragraph">
<p>For this, we recommend that you ensure that all such methods are tested through an <a href="ugtst.html#_ugtst_integ-test-support">integration test</a> (not unit test).</p>
</div>
<div class="paragraph">
<p>For example, here’s the corresponding integration test for the "Exclamation mark" example from the simpleapp (above):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Rule</span>
<span class="directive">public</span> ExpectedException expectedException = ExpectedException.none();
<span class="annotation">@Inject</span>
FixtureScripts fixtureScripts;
<span class="annotation">@Test</span>
<span class="directive">public</span> <span class="type">void</span> failsValidation() <span class="directive">throws</span> <span class="exception">Exception</span> {
<span class="comment">// given</span>
RecreateSimpleObjects fs = <span class="keyword">new</span> RecreateSimpleObjects().setNumber(<span class="integer">1</span>);
fixtureScripts.runFixtureScript(fs, <span class="predefined-constant">null</span>);
SimpleObject simpleObjectWrapped = wrap(fs.getSimpleObjects().get(<span class="integer">0</span>));
<span class="comment">// expect</span>
expectedExceptions.expect(InvalidException.class);
expectedExceptions.expectMessage(<span class="string"><span class="delimiter">"</span><span class="content">Exclamation mark is not allowed</span><span class="delimiter">"</span></span>);
<span class="comment">// when</span>
simpleObjectWrapped.updateName(<span class="string"><span class="delimiter">"</span><span class="content">new name!</span><span class="delimiter">"</span></span>);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Running this test will result in the framework calling the <code>validateUpdateName(…​)</code> method, and thus to record that a translation is required in the <code>.pot</code> file.</p>
</div>
<div class="paragraph">
<p>When the integration tests are complete (that is, when Apache Isis is shutdown), the <code>TranslationServicePo</code> will write out all captured translations to its log (more on this below). This will include all the translations captured from the Apache Isis metamodel, along with all translations as exercised by the integration tests.</p>
</div>
<div class="paragraph">
<p>To ensure your app is fully internationalized app, you must therefore:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>use <code>TranslatableString</code> rather than <code>String</code> for all validation/disable and title methods.</p>
</li>
<li>
<p>ensure that (at a minimum) all validation messages and title methods are integration tested.</p>
</li>
</ul>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>We make no apologies for this requirement: one of the reasons that we decided to implement Apache Isis' i18n support in this way is because it encourages/requires the app to be properly tested.</p>
</div>
<div class="paragraph">
<p>Behind the scenes Apache Isis uses a JUnit rule (<code>ExceptionRecognizerTranslate</code>) to intercept any exceptions that are thrown. These are simply passed through to the registered <a href="rgsvc.html#_rgsvc_spi_ExceptionRecognizer"><code>ExceptionRecognizer</code></a>s so that any messages are recorded as requiring translation.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_escaped_strings">4.6. Escaped strings</h3>
<div class="paragraph">
<p>Translated messages can be escaped if required, eg to include embedded markup.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="ini">#: com.mycompany.myapp.OrderItem#quantity
msgid "<i>Quantity</i>"
msgstr "<i>Quantité</i>"</code></pre>
</div>
</div>
<div class="paragraph">
<p>For this to work, the <code>namedEscaped()</code> attribute must be specified using either the
<a href="ugfun.html#_ugfun_object-layout_dynamic">dynamic layout</a> json file, or using an annotation such as
<a href="rgant.html#_rgant-PropertyLayout"><code>@PropertyLayout</code></a> or <a href="rgant.html#_rgant-ParameterLayout"><code>@ParameterLayout</code></a>.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@ParameterLayout</span>(
named=<span class="string"><span class="delimiter">"</span><span class="content"><i>Quantity</i></span><span class="delimiter">"</span></span>, <i class="conum" data-value="1"></i><b>(1)</b>
namedEscaped=<span class="predefined-constant">false</span>
)
<span class="directive">public</span> <span class="predefined-type">Integer</span> getQuantity() { ... }</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>required (even though it won’t be used when a translation is read; otherwise the escaped flag is ignored)</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_i18n_configuration">4.7. Configuration</h3>
<div class="paragraph">
<p>There are several different aspects of the translation service that can be configured.</p>
</div>
<div class="sect3">
<h4 id="_logging">4.7.1. Logging</h4>
<div class="paragraph">
<p>To configure the <code>TranslationServicePo</code> to write to out the <code>translations.pot</code> file, add the following to the <em>integtests</em> <code>logging.properties</code> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="ini">log4j.appender.translations-po=org.apache.log4j.FileAppender
log4j.appender.translations-po.File=./translations.pot
log4j.appender.translations-po.Append=false
log4j.appender.translations-po.layout=org.apache.log4j.PatternLayout
log4j.appender.translations-po.layout.ConversionPattern=%m%n
log4j.logger.org.apache.isis.core.runtime.services.i18n.po.PoWriter=INFO,translations-po
log4j.additivity.org.apache.isis.core.runtime.services.i18n.po.PotWriter=false</code></pre>
</div>
</div>
<div class="paragraph">
<p>Just to repeat, this is <em>not</em> the <code>WEB-INF/logging.properties</code> file, it should instead be added to the <code>integtests/logging.properties</code> file.</p>
</div>
</div>
<div class="sect3">
<h4 id="_location_of_the_code_po_code_files">4.7.2. Location of the <code>.po</code> files</h4>
<div class="paragraph">
<p>The default location of the translated <code>.po</code> files is in the <code>WEB-INF</code> directory. These are named and searched for similarly to regular Java resource bundles.</p>
</div>
<div class="paragraph">
<p>For example, assuming these translations:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="ini">/WEB-INF/translations-en-US.po
/translations-en.po
/translations-fr-FR.po
/translations.po</code></pre>
</div>
</div>
<div class="paragraph">
<p>then:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>a user with <code>en-US</code> locale will use <code>translations-en-US.po</code></p>
</li>
<li>
<p>a user with <code>en-GB</code> locale will use <code>translations-en.po</code></p>
</li>
<li>
<p>a user with <code>fr-FR</code> locale will use <code>translations-fr-FR.po</code></p>
</li>
<li>
<p>a user with <code>fr-CA</code> locale will use <code>translations.po</code></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The basename for translation files is always <code>translations</code>; this cannot be altered.</p>
</div>
</div>
<div class="sect3">
<h4 id="_externalized_translation_files">4.7.3. Externalized translation files</h4>
<div class="paragraph">
<p>Normally Apache Isis configuration files are read from the <code>WEB-INF</code> file. However, Apache Isis can be configured to read config files from an <a href="#_ugbtb_deployment_externalized-configuration">external directory</a>; this is also supported for translations.</p>
</div>
<div class="paragraph">
<p>Thus, if in <code>web.xml</code> the external configuration directory has been set:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><context-param></span>
<span class="tag"><param-name></span>isis.config.dir<span class="tag"></param-name></span>
<span class="tag"><param-value></span>location of external config directory<span class="tag"></param-value></span>
<span class="tag"></context-param></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Then this directory will be used as the base for searching for translations (rather than the default 'WEB-INF/' directory).</p>
</div>
</div>
<div class="sect3">
<h4 id="_force_read_mode">4.7.4. Force read mode</h4>
<div class="paragraph">
<p>As noted above, if running in prototype mode then <code>TranslationServicePo</code> will be in write mode, if in production mode then will be in read mode. To force read mode (ie use translations) even if in prototype mode, add the following configuration property to <code>isis.properties</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="ini">isis.services.translation.po.mode=read</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_supporting_services">4.8. Supporting services</h3>
<div class="paragraph">
<p>The <code>TranslationServicePo</code> has a number of supporting/related services.</p>
</div>
<div class="sect3">
<h4 id="__code_localeprovider_code">4.8.1. <code>LocaleProvider</code></h4>
<div class="paragraph">
<p>The <code>LocaleProvider</code> API is used by the <code>TranslationServicePo</code> implementation to obtain the locale of the "current user".</p>
</div>
<div class="paragraph">
<p>A default implementation is provided by the Wicket viewer.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Note that this default implementation does not support requests made through the Restful Objects viewer (there is no Wicket 'application' object available); the upshot is that requests through Restful Objects are never translated. Registering a different implementation of <code>LocaleProvider</code> that taps into appropriate REST (RestEasy?) APIs would be the way to address this.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="__code_translationsresolver_code">4.8.2. <code>TranslationsResolver</code></h4>
<div class="paragraph">
<p>The <code>TranslationResolver</code> is used by the <code>TranslationService</code> implementation to lookup translations for a specified locale. It is this service that reads from the <code>WEB-INF/</code> (or externalized directory).</p>
</div>
</div>
<div class="sect3">
<h4 id="__code_translationservicepomenu_code">4.8.3. <code>TranslationServicePoMenu</code></h4>
<div class="paragraph">
<p>The <code>TranslationServicePoMenu</code> provides a couple of menu actions in the UI (prototype mode only) that interacts with the underlying <code>TranslationServicePo</code>:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>the <code>downloadTranslationsFile()</code> action - available only in write mode - allows the current <code>.pot</code> file to be downloaded.<br></p>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>While this will contain all the translations from the metamodel, it will not necessarily contain all translations for all imperative methods returning <code>TranslatableString</code> instances; which are present and which are missing will depend on which imperative methods have been called (recorded by the service) prior to downloading.</p>
</div>
</td>
</tr>
</table>
</div>
</li>
<li>
<p>the <code>clearTranslationsCache()</code> action - available only in read mode - will clear the cache so that new translations can be loaded.<br></p>
<div class="paragraph">
<p>This allows a translator to edit the appropriate <code>translations-xx-XX.po</code> file and check the translation is correct without having to restart the app.</p>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_ugbtb_headless-access">5. Headless access</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This section tackles the topic of enabling access to an Isis application directly, or at least, not through either the <a href="ugvw.html">Wicket</a> or <a href="ugvro.html">Restful</a> viewers.</p>
</div>
<div class="paragraph">
<p>There are several main use-cases:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>enabling background execution, eg of a thread managed by Quartz scheduler and running within the webapp</p>
</li>
<li>
<p>integration from other systems, eg for a subscriber on a pub/sub mechanism such as Camel, pushing changes through an Apache Isis domain model.</p>
</li>
<li>
<p>leveraging an Isis application within a batch process</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Note that the calling thread runs in the same process space as the Apache Isis domain object model (must be physically linked to the JAR files containing the domain classes). For use cases where the calling thread runs in some other process space (eg migrating data from a legacy system), then the <a href="ugvro.html">Restful Objects viewer</a> is usually the way to go.</p>
</div>
<div class="paragraph">
<p>The API described in this chapter is reasonably low-level, allowing code to interact very directly with the Apache Isis metamodel and runtime. Such callers should be considered trusted: they do not (by default) honour any business rules eg implicit in the Isis annotations or hide/disable/validate methods. However the <a href="rgsvc.html#_rgsvc_api_WrapperFactory"><code>WrapperFactory</code></a> service could be used to enforce such business rules if required.</p>
</div>
<div class="sect2">
<h3 id="_ugbtb_headless-access_AbstractIsisSessionTemplate">5.1. AbstractIsisSessionTemplate</h3>
<div class="paragraph">
<p>The <code>AbstractIsisSessionTemplate</code> class (whose name is inspired by the Spring framework’s naming convention for similar classes that query <a href="http://docs.spring.io/spring/docs/2.5.x/reference/jdbc.html#jdbc-JdbcTemplate">JDBC</a>, <a href="http://docs.spring.io/spring/docs/2.5.x/reference/jms.html#jms-jmstemplate">JMS</a>, <a href="http://docs.spring.io/spring/docs/2.5.x/reference/orm.html#orm-jpa-template">JPA</a> etc.) provides the mechanism to open up a 'session' within the Apache Isis framework, in order to resolve and interact with entities.</p>
</div>
<div class="paragraph">
<p>The class itself is intended to be subclassed:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="directive">abstract</span> <span class="type">class</span> <span class="class">AbstractIsisSessionTemplate</span> {
<span class="directive">public</span> <span class="type">void</span> execute(<span class="directive">final</span> AuthenticationSession authSession, <span class="directive">final</span> <span class="predefined-type">Object</span> context) { ... } <i class="conum" data-value="1"></i><b>(1)</b>
<span class="directive">protected</span> <span class="directive">abstract</span> <span class="type">void</span> doExecute(<span class="predefined-type">Object</span> context); <i class="conum" data-value="2"></i><b>(2)</b>
<span class="directive">protected</span> ObjectAdapter adapterFor(<span class="directive">final</span> <span class="predefined-type">Object</span> targetObject) { ... }
<span class="directive">protected</span> ObjectAdapter adapterFor(<span class="directive">final</span> RootOid rootOid) { ... }
<span class="directive">protected</span> PersistenceSession getPersistenceSession() { ... }
<span class="directive">protected</span> IsisTransactionManager getTransactionManager() { ... }
<span class="directive">protected</span> AdapterManager getAdapterManager() { ... }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td><code>execute(…​)</code> sets up the <code>IsisSession</code> and delegates to …​</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td><code>doExecute(…​)</code>, the mandatory hook method for subclasses to implement.
The passed object represents passes a context from the caller (eg the scheduler, cron job, JMS etc) that instantiated and executed the class.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The <code>protected</code> methods expose key internal APIs within Apache Isis, for the subclass to use as necessary.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>One notable feature of <code>AbstractIsisSessionTemplate</code> is that it will automatically inject any domain services into itself. Thus, it is relatively easy for the subclass to "reach into" the domain, through injected repository services.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_headless-access_BackgroundCommandExecution">5.2. BackgroundCommandExecution</h3>
<div class="paragraph">
<p>The <code>BackgroundCommandExecution</code> class (a subclass of <a href="#_ugbtb_headless-access_AbstractIsisSessionTemplate">AbstractIsisSessionTemplate</a>) is intended to simplify the execution of background <code>Command</code>s persisted by way of the <a href="rgsvc.html#_rgsvc_spi_CommandService"><code>CommandService</code></a> and the <a href="rgsvc.html#_rgsvc_spi_BackgroundCommandService"><code>BackgroundCommandService</code></a>.</p>
</div>
<div class="paragraph">
<p>Its signature is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="directive">abstract</span> <span class="type">class</span> <span class="class">BackgroundCommandExecution</span> <span class="directive">extends</span> AbstractIsisSessionTemplate {
<span class="directive">protected</span> <span class="type">void</span> doExecute(<span class="predefined-type">Object</span> context) { ... }
<span class="directive">protected</span> <span class="directive">abstract</span> <span class="predefined-type">List</span><? <span class="directive">extends</span> Command> findBackgroundCommandsToExecute(); <i class="conum" data-value="1"></i><b>(1)</b>
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td><code>findBackgroundCommandsToExecute()</code> is a mandatory hook method for subclasses to implement.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>This allows for different implementations of the <code>CommandService</code> and <code>BackgroundCommandService</code> to persist to wherever.</p>
</div>
<div class="paragraph">
<p>The diagram below (<a href="http://yuml.me/edit/363b335f">yuml.me/363b335f</a>) shows the dependencies between these various classes:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/headless-access/BackgroundCommandExecution.png" alt="BackgroundCommandExecution" width="400px">
</div>
<div class="title">Figure 1. Inheritance Hierarchy for <code>BackgroundCommandExecution</code></div>
</div>
<div class="sect3">
<h4 id="_background_execution">5.2.1. Background Execution</h4>
<div class="paragraph">
<p>The <code>BackgroundCommandExecutionFromBackgroundCommandServiceJdo</code> is a concrete subclass of <code>BackgroundCommandExecution</code> (see the <a href="rgsvc.html#_rgsvc_api_BackgroundService"><code>BackgroundCommandService</code></a>), the intended use being for the class to be instantiated regularly (eg every 10 seconds) by a scheduler such as <a href="http://quartz-scheduler.org">Quartz</a>) to poll for <code>Command</code>s to be executed, and then execute them.</p>
</div>
<div class="paragraph">
<p>This implementation queries for <code>Command</code>s persisted by the <a href="http://www.isisaddons.org/isis-module-command">Isis addons Command Module</a>'s implementations of <a href="rgsvc.html#_rgsvc_spi_CommandService"><code>CommandService</code></a> and <a href="rgsvc.html#_rgsvc_spi_BackgroundCommandService"><code>BackgroundCommandService</code></a> using the <code>BackgroundCommandServiceJdoRepository</code>.</p>
</div>
<div class="paragraph">
<p>The diagram below (<a href="http://yuml.me/edit/25343da1">yuml.me/25343da1</a>) shows the inheritance hierarchy for this class:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/headless-access/BackgroundCommandExecutionFromBackgroundCommandServiceJdo.png" alt="BackgroundCommandExecutionFromBackgroundCommandServiceJdo" width="500px">
</div>
<div class="title">Figure 2. Inheritance Hierarchy for <code>BackgroundCommandExecutionFromBackgroundCommandServiceJdo</code></div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_ugbtb_other-techniques">6. Other Techniques</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This chapter pulls together a number of more advanced techniques that we’ve discovered and developed while building our own Isis applications.</p>
</div>
<div class="sect2">
<h3 id="_ugbtb_other-techniques_mapping-rdbms-views">6.1. Mapping RDBMS Views</h3>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
TODO - as used in <a href="http://github.com/estatio/estatio">Estatio</a>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_other-techniques_transactions-and-errors">6.2. Transactions and Errors</h3>
<div class="paragraph">
<p>In Apache Isis, every interaction (action invocation or property edit) is automatically wrapped in a transaction,
and any repository query automatically does a flush before hand.</p>
</div>
<div class="paragraph">
<p>What that means is that there’s no need to explicitly start or commit transactions in Apache Isis; this will be done
for you. Indeed, if you do try to manage transactions (eg by reaching into the JDO <code>PersistenceManager</code> exposed by the
<a href="rgsvc.html#_rgsvc_api_IsisJdoSupport">IsisJdoSupport</a> domain service, then you are likely to confuse the
framework and get a stack trace for your trouble.</p>
</div>
<div class="paragraph">
<p>However, you can complete a given transaction and start a new one. This is sometimes useful if writing a fixture
script which is going to perform some sort of bulk migration of data from an old system. For this use case, use the
<a href="rgsvc.html#_rgsvc_api_TransactionService"><code>TransactionService</code></a>.</p>
</div>
<div class="paragraph">
<p>For example:</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">SomeLongRunningFixtureScript</span> <span class="directive">extends</span> FixtureScript
<span class="directive">protected</span> <span class="type">void</span> execute(<span class="directive">final</span> ExecutionContext executionContext) {
<span class="comment">// do some work</span>
transactionService.nextTransaction();
<span class="comment">// do some work</span>
transactionService.nextTransaction();
<span class="comment">// do yet more work</span>
}
<span class="annotation">@javax</span>.inject.Inject
TransactionService transactionService;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>You get the idea.</p>
</div>
<div class="sect3">
<h4 id="_raise_message_errors_to_users">6.2.1. Raise message/errors to users</h4>
<div class="paragraph">
<p>The framework provides the <a href="rgsvc.html#_rgsvc_api_MessageService"><code>MessageService</code></a> as a means to return an out-of-band
message to the end-user. In the <a href="ugvw.html">Wicket viewer</a> these are shown as "toast" pop-ups; the
<a href="ugvro.html">Restful Objects viewer</a> returns an HTTP header.</p>
</div>
<div class="paragraph">
<p>The <code>UserService</code> provides three APIs, for different:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>informUser()</code> - an informational message. In the Wicket viewer these are short-lived pop-ups that disappear after a short time.</p>
</li>
<li>
<p><code>warnUser()</code> - a warning. In the Wicket viewer these do not auto-close; they must be acknowledged.</p>
</li>
<li>
<p><code>raiseError()</code> - an error. In the Wicket viewer these do not auto-close; they must be acknowledged.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Each pop-up has a different background colour indicating its severity.</p>
</div>
<div class="paragraph">
<p>None of these messages/errors has any influence on the transaction; any changes to objects will be committed.</p>
</div>
</div>
<div class="sect3">
<h4 id="_aborting_transactions">6.2.2. Aborting transactions</h4>
<div class="paragraph">
<p>If you want to abort Apache Isis' transaction, this can be done by throwing an exception. The exception message
is displayed to the user on the error page (if <a href="ugvw.html">Wicket viewer</a>) or a 500 status error code (if the
<a href="ugvro.html">Restful Objects</a> viewer).</p>
</div>
<div class="paragraph">
<p>If the exception thrown is because of an unexpected error (eg a <code>NullPointerException</code> in the domain app itself), then
the error page will include a stack trace. If however you want to indicate that the exception is in some sense
"expected", then throw a <code>RecoverableException</code> (or any subclass, eg <code>ApplicationException</code>); the stack trace will then
be suppressed from the error page.</p>
</div>
<div class="paragraph">
<p>Another way in which exceptions might be considered "expected" could be as the result of attempting to persist an
object which then violates some type of database constraint. Even if the domain application checks beforehand, it
could be that another user operating on the object at the same moment of time might result in the conflict.</p>
</div>
<div class="paragraph">
<p>To handle this the <a href="rgsvc.html#_rgsvc_spi_ExceptionRecognizer"><code>ExceptionRecognizer</code></a> SPI can be used. The
framework provides a number of implementations out-of-the-box; whenever an exception is thrown it is passed to each
known recognizer implementation to see if it recognizes the exception and can return a user-meaningful error message.
For example, <code>ExceptionRecognizerForSQLIntegrityConstraintViolationUniqueOrIndexException</code> checks if the
exception inherits from <code>java.sql.SQLIntegrityConstraintViolationException</code>, and if so, constructs a suitable message.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_other-techniques_multi-tenancy">6.3. Multi-tenancy</h3>
<div class="paragraph">
<p>Of the various Isis Addons, the <a href="https://github.com/isisaddons/isis-module-security">security module</a> has the most features. One significant feature is the ability to associate users and objects with a "tenancy".</p>
</div>
<div class="paragraph">
<p>For more details, see the <a href="https://github.com/isisaddons/isis-module-security">security module</a> README.</p>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_other-techniques_persisted-title">6.4. Persisted Title</h3>
<div class="paragraph">
<p>Normally the title of an object is not persisted to the database, rather it is recomputed automatically from underlying properties. On occasion though you might want the title to also be persisted; either to make things easier for the DBA, or for an integration scenario, or some other purpose.</p>
</div>
<div class="paragraph">
<p>We can implement this feature by leveraging the <a href="rgcms.html#_rgcms_methods_lifecycle_jdo-api">JDO lifecycle</a>. In the design we discuss here we make it a responsibility of the entities to persist the title as a property, by implementing a <code>ObjectWithPersistedTitle</code> interface:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">interface</span> <span class="class">ObjectWithPersistedTitle</span> {
<span class="annotation">@PropertyLayout</span>(hidden = Where.EVERYWHERE) <i class="conum" data-value="1"></i><b>(1)</b>
<span class="predefined-type">String</span> getTitle();
<span class="type">void</span> setTitle(<span class="directive">final</span> <span class="predefined-type">String</span> title);
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>we don’t want to expose this in the UI because the title is already prominently displayed.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>We can then define a subscribing domain service that leverage this.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@DomainService</span>(nature = NatureOfService.DOMAIN)
<span class="directive">public</span> <span class="type">class</span> <span class="class">TitlingService</span> <span class="directive">extends</span> AbstractSubscriber {
<span class="annotation">@Subscribe</span>
<span class="directive">public</span> <span class="type">void</span> on(<span class="directive">final</span> ObjectPersistingEvent ev) {
handle(ev.getSource());
}
<span class="annotation">@Subscribe</span>
<span class="directive">public</span> <span class="type">void</span> on(<span class="directive">final</span> ObjectUpdatingEvent ev) {
handle(ev.getSource());
}
<span class="directive">private</span> <span class="type">void</span> handle(<span class="directive">final</span> <span class="predefined-type">Object</span> persistentInstance) {
<span class="keyword">if</span>(persistentInstance <span class="keyword">instanceof</span> ObjectWithPersistedTitle) {
<span class="directive">final</span> ObjectWithPersistedTitle objectWithPersistedTitle =
(ObjectWithPersistedTitle) persistentInstance;
objectWithPersistedTitle.setTitle(container.titleOf(objectWithPersistedTitle));
}
}
<span class="annotation">@Inject</span>
<span class="directive">private</span> DomainObjectContainer container;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Prior to 1.10.0 (when lifecycle events were introduced), this could also be be done by accessing the JDO API directly:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@RequestScoped</span>
<span class="annotation">@DomainService</span>(nature = NatureOfService.DOMAIN)
<span class="directive">public</span> <span class="type">class</span> <span class="class">TitlingService</span> {
<span class="annotation">@PostConstruct</span>
<span class="directive">public</span> <span class="type">void</span> init() {
isisJdoSupport.getJdoPersistenceManager().addInstanceLifecycleListener(
<span class="keyword">new</span> StoreLifecycleListener() {
<span class="annotation">@Override</span>
<span class="directive">public</span> <span class="type">void</span> preStore(<span class="directive">final</span> InstanceLifecycleEvent event) {
<span class="directive">final</span> <span class="predefined-type">Object</span> persistentInstance = event.getPersistentInstance();
<span class="keyword">if</span>(persistentInstance <span class="keyword">instanceof</span> ObjectWithPersistedTitle) {
<span class="directive">final</span> ObjectWithPersistedTitle objectWithPersistedTitle =
(ObjectWithPersistedTitle) persistentInstance;
objectWithPersistedTitle.setTitle(container.titleOf(objectWithPersistedTitle));
}
}
<span class="annotation">@Override</span>
<span class="directive">public</span> <span class="type">void</span> postStore(<span class="directive">final</span> InstanceLifecycleEvent event) {
}
}, <span class="predefined-constant">null</span>);
}
<span class="annotation">@Inject</span>
<span class="directive">private</span> IsisJdoSupport isisJdoSupport;
<span class="annotation">@Inject</span>
<span class="directive">private</span> DomainObjectContainer container;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The above is probably the easiest and most straightforward design. One could imagine other designs where the persisted title is stored elsewhere. It could even be stored off into an <a href="http://lucene.apache.org/">Apache Lucene</a> (or similar) database to allow for free-text searches.</p>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_other-techniques_replacing-default-service-implementations">6.5. Overriding Default Service Implns</h3>
<div class="paragraph">
<p>The framework provides default implementations for many of the <a href="#rgsvc.adoc">domain services</a>. This is convenient, but sometimes you will want to replace the default implementation with your own service implementation.</p>
</div>
<div class="paragraph">
<p>The trick is to use the <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> attribute, specifying a low number (typically <code>"1"</code>).</p>
</div>
<div class="paragraph">
<p>For example, suppose you wanted to provide your own implementation of <a href="rgsvc.html#_rgsvc_api_LocaleProvider"><code>LocaleProvider</code></a>. Here’s how:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@DomainService</span>(
nature = NatureOfService.DOMAIN
)
<span class="annotation">@DomainServiceLayout</span>(
menuOrder = <span class="string"><span class="delimiter">"</span><span class="content">1</span><span class="delimiter">"</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
)
<span class="directive">public</span> <span class="type">class</span> <span class="class">MyLocaleProvider</span> <span class="directive">implements</span> LocaleProvider {
<span class="annotation">@Override</span>
<span class="directive">public</span> <span class="predefined-type">Locale</span> getLocale() {
<span class="keyword">return</span> ...
}
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>takes precedence over the default implementation.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>It’s also quite common to want to decorate the existing implementation (ie have your own implementation delegate to the default); this is also possible and quite easy if using <code>1.10.0</code> or later:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@DomainService</span>(
nature = NatureOfService.DOMAIN
)
<span class="annotation">@DomainServiceLayout</span>(
menuOrder = <span class="string"><span class="delimiter">"</span><span class="content">1</span><span class="delimiter">"</span></span> <i class="conum" data-value="1"></i><b>(1)</b>
)
<span class="directive">public</span> <span class="type">class</span> <span class="class">MyLocaleProvider</span> <span class="directive">implements</span> LocaleProvider {
<span class="annotation">@Override</span>
<span class="directive">public</span> <span class="predefined-type">Locale</span> getLocale() {
<span class="keyword">return</span> getDelegateLocaleProvider().getLocale(); <i class="conum" data-value="2"></i><b>(2)</b>
}
Optional<LocaleProvider> delegateLocaleProvider; <i class="conum" data-value="3"></i><b>(3)</b>
<span class="directive">private</span> LocaleProvider getDelegateLocaleProvider() {
<span class="keyword">if</span>(delegateLocaleProvider == <span class="predefined-constant">null</span>) {
delegateLocaleProvider = Iterables.tryFind(localeProviders, input -> input != <span class="local-variable">this</span>); <i class="conum" data-value="4"></i><b>(4)</b>
}
<span class="keyword">return</span> delegateLocaleProvider.orNull();
}
<span class="annotation">@Inject</span>
<span class="predefined-type">List</span><LocaleProvider> localeProviders; <i class="conum" data-value="5"></i><b>(5)</b>
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>takes precedence over the default implementation when injected elsewhere.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>this implementation merely delegates to the default implementation</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>lazily populated</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>delegate to the first implementation that isn’t <em>this</em> implementation (else infinite loop!)</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Injects all implementations, including this implemenation</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_ugbtb_programming-model">7. Customizing the Prog Model</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This chapter explains the main APIs to extend or alter the programming conventions that Apache Isis understands to
build up its metamodel.</p>
</div>
<div class="sect2">
<h3 id="_ugbtb_programming-model_custom-validator">7.1. Custom validator</h3>
<div class="paragraph">
<p>Apache Isis' programming model includes a validator component that detects and prevents (by failing fast) a number of situations where the domain model is logically inconsistent.</p>
</div>
<div class="paragraph">
<p>For example, the validator will detect any orphaned supporting methods (eg <code>hideXxx()</code>) if the corresponding property or action has been renamed or deleted but the supporting method was not also updated. Another example is that a class cannot have a title specified both using <code>title()</code> method and also using <code>@Title</code> annotation.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The support for <a href="rgant.html#_rgant-aaa_deprecated">disallowing deprecated annotations</a> is also implemented using the metamodel validator.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>You can also impose your own application-specific rules by installing your own metamodel validator. To give just one example, you could impose naming standards such as ensuring that a domain-specific abbreviation such as "ISBN" is always consistently capitalized wherever it appears in a class member.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Isis' <a href="#rgmvn.adoc">Maven plugin</a> will also validate the domain object model during build time.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_api_and_implementation">7.1.1. API and Implementation</h4>
<div class="paragraph">
<p>There are several ways to go about implementing a validator.</p>
</div>
<div class="sect4">
<h5 id="__code_metamodelvalidator_code"><code>MetaModelValidator</code></h5>
<div class="paragraph">
<p>Any custom validator must implement Apache Isis' internal <code>MetaModelValidator</code> interface, so the simplest option is just to implement <code>MetaModelValidator</code> directly:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">interface</span> <span class="class">MetaModelValidator</span>
<span class="directive">implements</span> SpecificationLoaderSpiAware { <i class="conum" data-value="1"></i><b>(1)</b>
<span class="directive">public</span> <span class="type">void</span> validate(
ValidationFailures validationFailures); <i class="conum" data-value="2"></i><b>(2)</b>
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>the <code>SpecificationLoader</code> is the internal API providing access to the Apache Isis metamodel.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>add any metamodel violations to the <code>ValidationFailures</code> parameter (the <a href="http://c2.com/cgi/wiki?CollectingParameter">collecting parameter</a> pattern)</td>
</tr>
</table>
</div>
</div>
<div class="sect4">
<h5 id="__code_visitor_code"><code>Visitor</code></h5>
<div class="paragraph">
<p>More often than not, you’ll want to visit every element in the metamodel, and so for this you can instead subclass from <code>MetaModelValidatorVisiting.Visitor</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="directive">final</span> <span class="type">class</span> <span class="class">MetaModelValidatorVisiting</span> ... {
<span class="directive">public</span> <span class="directive">static</span> <span class="type">interface</span> <span class="class">Visitor</span> {
<span class="directive">public</span> <span class="type">boolean</span> visit( <i class="conum" data-value="1"></i><b>(1)</b>
ObjectSpecification objectSpec, <i class="conum" data-value="2"></i><b>(2)</b>
ValidationFailures validationFailures); <i class="conum" data-value="3"></i><b>(3)</b>
}
...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>return <code>true</code> continue visiting specs.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td><code>ObjectSpecification</code> is the internal API representing a class</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>add any metamodel violations to the <code>ValidationFailures</code> parameter</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>You can then create your custom validator by subclassing <code>MetaModelValidatorComposite</code> and adding the visiting validator:</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">MyMetaModelValidator</span> <span class="directive">extends</span> MetaModelValidatorComposite {
<span class="directive">public</span> MyMetaModelValidator() {
add(<span class="keyword">new</span> MetaModelValidatorVisiting(<span class="keyword">new</span> MyVisitor()));
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>If you have more than one rule then each can live in its own visitor.</p>
</div>
</div>
<div class="sect4">
<h5 id="__code_summarizingvisitor_code"><code>SummarizingVisitor</code></h5>
<div class="paragraph">
<p>As a slight refinement, you can also subclass from <code>MetaModelValidatorVisiting.SummarizingVisitor</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="directive">final</span> <span class="type">class</span> <span class="class">MetaModelValidatorVisiting</span> ... {
<span class="directive">public</span> <span class="directive">static</span> <span class="type">interface</span> <span class="class">SummarizingVisitor</span> <span class="directive">extends</span> Visitor {
<span class="directive">public</span> <span class="type">void</span> summarize(ValidationFailures validationFailures);
}
...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>A <code>SummarizingVisitor</code> will be called once after every element in the metamodel has been visited. This is great for performing checks on the metamodel as a whole. For example, Apache Isis uses this to check that there is at least one <code>@Persistable</code> domain entity defined.</p>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_configuration">7.1.2. Configuration</h4>
<div class="paragraph">
<p>Once you have implemented your validator, you must register it with the framework by defining the appropriate configuration property:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="ini">isis.reflector.validator=com.mycompany.myapp.MyMetaModelValidator</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_programming-model_finetuning">7.2. Finetuning</h3>
<div class="paragraph">
<p>The core metamodel defines APIs and implementations for building the Apache Isis metamodel: a description of the set of entities, domain services and values that make up the domain model.</p>
</div>
<div class="paragraph">
<p>The description of each domain class consists of a number of elements:</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1"><a href="https://github.com/apache/isis/blob/master/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecification.java">ObjectSpecification</a></dt>
<dd>
<p>Analogous to <code>java.lang.Class</code>; holds information about the class itself and holds collections of each of the three types of class' members (below);</p>
</dd>
<dt class="hdlist1"><a href="https://github.com/apache/isis/blob/master/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/OneToOneAssociation.java">OneToOneAssociation</a></dt>
<dd>
<p>Represents a class member that is a single-valued property of the class. The property’s type is either a reference to another entity, or is a value type.</p>
</dd>
<dt class="hdlist1"><a href="https://github.com/apache/isis/blob/master/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/OneToManyAssociation.java">OneToManyAssociation</a></dt>
<dd>
<p>Represents a class member that is a collection of references to other entities. Note that Apache Isis does not currently support collections of values.</p>
</dd>
<dt class="hdlist1"><a href="https://github.com/apache/isis/blob/master/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAction.java">ObjectAction</a></dt>
<dd>
<p>Represents a class member that is an operation that can be performed on the action. Returns either a single value, a collection of entities, or is <code>void</code>.</p>
</dd>
</dl>
</div>
<div class="paragraph">
<p>The metamodel is built up through the <a href="https://github.com/apache/isis/blob/master/core/metamodel/src/main/java/org/apache/isis/core/metamodel/progmodel/ProgrammingModel.java">ProgrammingModel</a>, which defines an API for registering a set of <a href="https://github.com/apache/isis/blob/master/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/FacetFactory.java">FacetFactory</a>s. Two special <code>FacetFactory</code> implementations - <a href="https://github.com/apache/isis/blob/master/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/accessor/PropertyAccessorFacetViaAccessorFactory.java">PropertyAccessorFacetFactory</a> and <a href="https://github.com/apache/isis/blob/master/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/accessor/CollectionAccessorFacetViaAccessorFactory.java">CollectionAccessorFacetFactory</a> - are used to identify the class members.
Pretty much all the other <code>FacetFactory</code>s are responsible for installing <a href="https://github.com/apache/isis/blob/master/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/Facet.java">Facet</a>s onto the metamodel elements.</p>
</div>
<div class="paragraph">
<p>There are many many such <code>Facet</code>s, and are used to do such things get values from properties or collections, modify properties or collections, invoke action, hide or disable class members, provide UI hints for class members, and so on. In short, the <code>FacetFactory</code>s registered defines the Apache Isis programming conventions.</p>
</div>
<div class="sect3">
<h4 id="_modifying_the_prog_model">7.2.1. Modifying the Prog. Model</h4>
<div class="paragraph">
<p>The default implementation of <code>ProgrammingModel</code> is <a href="https://github.com/apache/isis/blob/master/core/metamodel/src/main/java/org/apache/isis/progmodels/dflt/ProgrammingModelFacetsJava5.java">ProgrammingModelFacetsJava5</a>, which registers a large number of <code>FacetFactory</code>s.</p>
</div>
<div class="paragraph">
<p>By editing <code>isis.properties</code> you can modify the programming conventions either by (a) using the default programming model, but tweaking it to include new `FacetFactory`s or exclude existing, or (b) by specifying a completely different programming model implementation.</p>
</div>
<div class="paragraph">
<p>Let’s see how this is done.</p>
</div>
<div class="sect4">
<h5 id="_including_or_excluding_facets">Including or excluding facets</h5>
<div class="paragraph">
<p>Suppose that you wanted to completely remove support for the (already deprecated) <code>@ActionOrder</code> annotation. This would be done using:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="ini">isis.reflector.facets.exclude=org.apache.isis.core.metamodel.facets.object.actionorder.annotation.ActionOrderFacetAnnotationFactory</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or, suppose you wanted to use the example <a href="https://github.com/apache/isis/blob/master/mothballed/misc/metamodel/namefile/src/main/java/org/apache/isis/example/metamodel/namefile/facets/NameFileFacetFactory.java">"namefile"</a> <code>FacetFactory</code> as part of your programming conventions, use:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="ini">isis.reflector.facets.include=org.apache.isis.example.metamodel.namefile.facets.NameFileFacetFactory</code></pre>
</div>
</div>
<div class="paragraph">
<p>To include/exclude more than one <code>FacetFactory</code>, specify as a comma-separated list.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>This <a href="http://isis.markmail.org/thread/472c3mrvcgnripst">thread</a> from the users mailing list (in Apr 2014) shows a typical customization (to enable per-instance security) (though note that <a href="#_ugbtb_other-techniques_multi-tenancy">Multi-Tenancy</a> is now a better solution to that particular use-case.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Previously (prior to <code>1.13.0</code>) the framework also supported the "isis.reflector.facets" configuration property, to
specify a completely new implementation of the (internal API) <code>ProgrammingModel</code>. This is no longer supported; use
"isis.reflector.facets.include" and "isis.reflector.facet.exclude" to adjust the framework’s default implementation
(called <code>ProgrammingModelFacetsJava5</code>).</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_programming-model_layout-metadata-reader">7.3. Layout Metadata Reader</h3>
<div class="paragraph">
<p>The metadata for domain objects is obtained both <a href="ugfun.html#_ugfun_object-layout_static">statically</a> and <a href="ugfun.html#_ugfun_object-layout_dynamic">dynamically</a>.</p>
</div>
<div class="paragraph">
<p>The default implementation for reading dynamic layout metadata is <code>org.apache.isis.core.metamodel.layoutmetadata.json.LayoutMetadataReaderFromJson</code>, which is responsible for reading from the <code>Xxx.layout.json</code> files on the classpath (for each domain entity <code>Xxx</code>).</p>
</div>
<div class="paragraph">
<p>You can also implement your own metadata readers and plug them into Apache Isis. These could read from a different file format, or they could, even, read data dynamically from a URL or database. (Indeed, one could imagine an implementation whereby users could share layouts, all stored in some central repository).</p>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The use of dynamic layouts through the <code>.layout.json</code> - and therefore also the <code>LayoutMetadataReader</code> - is DEPRECATED. Instead, the <a href="ugfun.html#_ugfun_object-layout_dynamic_xml">dynamic XML layouts</a> using <code>.layout.xml</code> enables much more sophisticated custom layouts than those afforded by <code>.layout.json</code>.</p>
</div>
<div class="paragraph">
<p>By default, custom XML layouts are read from the classpath. This behaviour can be customized by providing an
alternative implementation of the <a href="rgsvc.html#_rgsvc_spi_GridLoaderService"><code>GridLoaderService</code></a>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_api_and_implementation_2">7.3.1. API and Implementation</h4>
<div class="paragraph">
<p>Any reader must implement Apache Isis' internal <code>LayoutMetadataReader</code> interface:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">interface</span> <span class="class">LayoutMetadataReader</span> {
<span class="directive">public</span> <span class="predefined-type">Properties</span> asProperties(<span class="predefined-type">Class</span><?> domainClass) <span class="directive">throws</span> ReaderException;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The implementation "simply" returns a set of properties where the property key is a unique identifier to both the class member and also the facet of the class member to which the metadata relates.</p>
</div>
<div class="paragraph">
<p>See the implementation of the built-in <code>LayoutMetadataReaderFromJson</code> for more detail.</p>
</div>
<div class="paragraph">
<p>Returning either <code>null</code> or throwing an exception indicates that the reader was unable to load any metadata for the specified class.</p>
</div>
<div class="sect4">
<h5 id="_extended_api">Extended API</h5>
<div class="paragraph">
<p>Optionally the reader can implement the extended <code>LayoutMetadaReader2</code> API:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">interface</span> <span class="class">LayoutMetadataReader2</span> <span class="directive">extends</span> LayoutMetadataReader {
<span class="directive">public</span> <span class="directive">static</span> <span class="type">class</span> <span class="class">Support</span> {
<span class="directive">public</span> <span class="directive">static</span> Support entitiesOnly() {
<span class="keyword">return</span> <span class="keyword">new</span> Support(<span class="predefined-constant">false</span>,<span class="predefined-constant">false</span>,<span class="predefined-constant">false</span>,<span class="predefined-constant">false</span>,<span class="predefined-constant">false</span>,<span class="predefined-constant">false</span>,<span class="predefined-constant">false</span>);
}
...
public <span class="type">boolean</span> interfaces() { ... } <i class="conum" data-value="1"></i><b>(1)</b>
<span class="directive">public</span> <span class="type">boolean</span> anonymous() { ... } <i class="conum" data-value="2"></i><b>(2)</b>
<span class="directive">public</span> <span class="type">boolean</span> synthetic() { ... } <i class="conum" data-value="3"></i><b>(3)</b>
<span class="directive">public</span> <span class="type">boolean</span> array() { ... } <i class="conum" data-value="4"></i><b>(4)</b>
<span class="directive">public</span> <span class="type">boolean</span> enums() { ... } <i class="conum" data-value="5"></i><b>(5)</b>
<span class="directive">public</span> <span class="type">boolean</span> applibValueTypes() { <i class="conum" data-value="6"></i><b>(6)</b>
<span class="directive">public</span> <span class="type">boolean</span> services() { ... } <i class="conum" data-value="7"></i><b>(7)</b>
}
Support support();
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>whether this implementation can provide metadata for interface types.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>whether this implementation can provide metadata for anonymous classes.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>whether this implementation can provide metadata for synthetic types.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>whether this implementation can provide metadata for arrays.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>whether this implementation can provide metadata for enums.</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>whether this implementation can provide metadata for applib value types.</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>whether this implementation can provide metadata for domain services.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The <code>support()</code> method returns a struct class that describes the types of classes are supported by this implementation.</p>
</div>
<div class="paragraph">
<p>The <code>LayoutMetadataReaderFromJson</code> implements this extended API.</p>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_configuration_2">7.3.2. Configuration</h4>
<div class="paragraph">
<p>Once you have implemented your validator, you must register it with the framework by defining the appropriate configuration property:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="ini">isis.reflector.layoutMetadataReaders=\
com.mycompany.myapp.MyMetaModelValidator,\
org.apache.isis.core.metamodel.layoutmetadata.json.LayoutMetadataReaderFromJson <i class="conum" data-value="1"></i><b>(1)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>the property replaces any existing metadata readers; if you want to preserve the ability to read from <code>Xxx.layout.json</code> then also register Apache Isis' built-in implementation.</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_ugbtb_deployment">8. Deployment</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This chapter provides guidance on some common deployment scenarios.</p>
</div>
<div class="sect2">
<h3 id="_ugbtb_deployment_cmd-line">8.1. Command Line (<code>WebServer</code>)</h3>
<div class="paragraph">
<p>As well as deploying an Apache Isis application into a servlet container, it is also possible to invoke from the command line using the <code>org.apache.isis.WebServer</code> utility class. This is especially useful while developing and testing, but may also suit some deployment scenarios (eg running as a standalone EXE within a Docker container, for example). Internally the <code>WebServer</code> spins up a Jetty servlet container.</p>
</div>
<div class="paragraph">
<p>The class also supports a number of command line arguments:</p>
</div>
<table class="tableblock frame-all grid-all spread">
<caption class="title">Table 2. Command line args for <code>org.apache.isis.Webserver</code></caption>
<colgroup>
<col style="width: 11.1111%;">
<col style="width: 22.2222%;">
<col style="width: 22.2222%;">
<col style="width: 44.4445%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Flag</th>
<th class="tableblock halign-left valign-top">Long format</th>
<th class="tableblock halign-left valign-top">Values<br>
(default)</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p><code>-t</code></p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p><code>--type</code></p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p><code>server_prototype</code>, <code>server</code><br>
(<code>server</code>)</p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>Deployment type</p>
</div></div></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p><code>-m</code></p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p><code>--manifest</code></p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>FQCN</p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>Fully qualified class name of the <a href="rgcms.html#_rgcms_classes_super_AppManifest"><code>AppManifest</code></a> to use to bootstrap the system.<br></p>
</div>
<div class="paragraph">
<p>This flag sets/overrides the <code>isis.appManifest</code> configuration property to the specified class name.</p>
</div></div></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p><code>-f</code></p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p><code>--fixture</code></p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>FQCN</p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>Fully qualified class name of the fixture (extending <a href="rgcms.html#_rgcms_classes_super_FixtureScript"><code>FixtureScript</code></a>) to be run to setup data.<br></p>
</div>
<div class="paragraph">
<p>This flag sets/overrides the <code>isis.fixtures</code> configuration property to the specified class name, and also sets the <code>isis.persistor.datanucleus.install-fixtures</code> configuration property to <code>true</code> to instruct the JDO/DataNucleus objectstore to actually load in the fixtures.<br></p>
</div>
<div class="paragraph">
<p>It is also possible to specify the fixture class name using either the <code>$IsisFixture</code> or <code>$IsisFixtures</code> environment variable (case insensitive).</p>
</div></div></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p><code>-p</code></p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p><code>--port</code></p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>(8080)</p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>The port number to listen on.<br></p>
</div>
<div class="paragraph">
<p>This flag sets/overrides the <code>isis.embedded-web-server.port</code> configuration property.</p>
</div></div></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p><code>-c</code></p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p><code>--config</code></p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>filename</p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>configuration file containing additional configuration properties</p>
</div></div></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p><code>-D</code></p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>xxx=yyy</p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>Specify additional arbitrary configuration properties. This can be specified multiple times.<br></p>
</div>
<div class="paragraph">
<p>Further discussion below.</p>
</div></div></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p><code>-v</code></p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p><code>--version</code></p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>Prints the version, then quits</p>
</div></div></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p><code>-h</code></p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p><code>--help</code></p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div></div></td>
<td class="tableblock halign-left valign-top"><div><div class="paragraph">
<p>Prints a usage message, then quits</p>
</div></div></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>Note that the <code>-D</code> argument is <strong>not</strong> a system property, it is parsed by <code>WebServer</code> itself. That said, it is <em>also</em> possible to specify system properties, and these will also be processed in the exact same way.<br></p>
</div>
<div class="paragraph">
<p>Said another way, properties can be specified either as application arguments:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="ini">java org.apache.isis.WebServer -Dxxx=yyy -Daaa=bbb</code></pre>
</div>
</div>
<div class="paragraph">
<p>or as system properties:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="ini">java -Dxxx=yyy -Daaa=bbb org.apache.isis.WebServer</code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="title">The <code>Dummy</code> class</div>
<div class="paragraph">
<p>The framework also provides the <code>org.apache.isis.Dummy</code> class, which consists of a single empty <code>main(String[])</code> method. It was introduced as a convenience for developers using Eclipse in combination with the DataNucleus plugin; if used as a launch target then it allow the entities to be enhanced withouth the running an app.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_deployment_tomcat">8.2. Deploying to Tomcat</h3>
<div class="paragraph">
<p>Some pointers when deploying to Tomcat (or any other servlet container).</p>
</div>
<div class="sect3">
<h4 id="_externalized_configuration">8.2.1. Externalized Configuration</h4>
<div class="paragraph">
<p>See the guidance <a href="#_ugbtb_deployment_externalized-configuration">below</a>.</p>
</div>
</div>
<div class="sect3">
<h4 id="_jvm_args">8.2.2. JVM Args</h4>
<div class="paragraph">
<p>The <a href="rgsvc.html#_rgsvc_api_WrapperFactory"><code>WrapperFactory</code></a> uses <a href="http://www.javassist.org">Javassist</a> to create on-the-fly classes acting as a proxy. The cost of these proxies can be mitigated using:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="ini">-XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_using_a_jndi_datasource">8.2.3. Using a JNDI Datasource</h4>
<div class="paragraph">
<p>See the guidance in the <a href="rgcfg.html#_rgcfg_configuring-datanucleus_using-jndi-data-source">configuring datanucleus</a> section.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_deployment_externalized-configuration">8.3. Externalized Configuration</h3>
<div class="paragraph">
<p>As described <a href="rgcfg.html#_rgcfg_configuration-files">here</a>, by default Apache Isis itself bootstraps from the
<code>isis.properties</code> configuration file. It will also read configuration from the (optional)
component/implementation-specific configuration files (such as
<code>persistor_datanucleus.properties</code> or <code>viewer_wicket.properties</code>), and also (optional) component-specific configuration
files (such as <code>persistor.properties</code> or <code>viewer.properties</code>).</p>
</div>
<div class="paragraph">
<p>It’s generally not good practice to have the same configuration property in more than one file, but if that does occur,
then the subsequent configuration property will be ignored.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>In addition the framework will also load the <code>overrides.properties</code> file. This is intended to support deployment
through Docker, as discussed <a href="#_ugbtb_deployment_docker">next</a>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>All of these files are read from the <code>WEB-INF</code> directory. Having this configuration "baked into" the application is
okay in a development environment, but when the app needs to be deployed to a test or production environment, this
configuration should be read from an external location.</p>
</div>
<div class="paragraph">
<p>There are in fact several frameworks involved here, all of which need to be pointed to this external location:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Apache Isis itself, which (as already discussed) reads <code>isis.properties</code> and optional component-specific config files.</p>
</li>
<li>
<p><a href="http://shiro.apache.org">Apache Shiro</a>, which reads the <code>shiro.ini</code> file (and may read other files referenced from that file)</p>
</li>
<li>
<p><a href="http://logging.apache.org/log4j/1.2">Apache log4j 1.2</a>, for logging, which reads <code>logging.properties</code> file</p>
</li>
<li>
<p>although not used by Apache Isis, there’s a good chance you may be using the Spring framework (eg if using <a href="http://activemq.apache.org">Apache Active MQ</a> or <a href="http://camel.apache.org">Apache Camel</a>.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Each of these frameworks has its own way of externalizing its configuration.</p>
</div>
<div class="sect3">
<h4 id="_ugbtb_deployment_externalized-configuration_Isis">8.3.1. Apache Isis' Config</h4>
<div class="paragraph">
<p>To tell Apache Isis to load configuration from an external directory, specify the <code>isis.config.dir</code> context parameter.</p>
</div>
<div class="paragraph">
<p>If the external configuration directory is fixed for all environments (systest, UAT, prod etc), then you can specify within the <code>web.xml</code> itself:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><context-param></span>
<span class="tag"><param-name></span>isis.config.dir<span class="tag"></param-name></span>
<span class="tag"><param-value></span>location of external config directory<span class="tag"></param-value></span>
<span class="tag"></context-param></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>If however the configuration directory varies by environment, then the context parameter will be specified to each installation of your servlet container. Most (if not all) servlet containers will provide a means to define context parameters through proprietary config files.</p>
</div>
<div class="paragraph">
<p>For example, if using Tomcat 7.0, you would typically copy the empty <code>$TOMCAT_HOME/webapps/conf/context.xml</code> to a file specific to the webapp, for example <code>$TOMCAT_HOME/webapps/conf/todo.xml</code>. The context parameter would then be specified by adding the following:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><Parameter</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">isis.config.dir</span><span class="delimiter">"</span></span>
<span class="attribute-name">value</span>=<span class="string"><span class="delimiter">"</span><span class="content">/usr/local/tomcat/myapp/conf/</span><span class="delimiter">"</span></span>
<span class="attribute-name">override</span>=<span class="string"><span class="delimiter">"</span><span class="content">false</span><span class="delimiter">"</span></span><span class="tag">/></span></code></pre>
</div>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Note that the <code>override</code> key should be set to "false", not "true". It indicates whether the application’s own <code>web.xml</code> can override the setting. In most cases, you probably want to disallow that.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>For more detail, see the Tomcat documentation on <a href="http://tomcat.apache.org/tomcat-7.0-doc/config/context.html#Defining_a_context">defining a context</a> and on <a href="http://tomcat.apache.org/tomcat-7.0-doc/config/context.html#Context_Parameters">context parameters</a>.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Note that running the app using Apache Isis' <code>org.apache.isis.WebServer</code> bootstrapper currently does not externalized Apache Isis configuration.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_ugbtb_deployment_externalized-configuration_Shiro">8.3.2. Shiro Config</h4>
<div class="paragraph">
<p>If using Apache Isis' <a href="ugsec.html#_ugsec_configuring-isis-to-use-shiro">Shiro integration</a> for authentication and/or authorization, note that it reads from the <code>shiro.ini</code> configuration file. By default this also resides in <code>WEB-INF</code>.</p>
</div>
<div class="paragraph">
<p>Similar to Apache Isis, Shiro lets this configuration directory be altered, by specifying the <code>shiroConfigLocations</code> context parameter.</p>
</div>
<div class="paragraph">
<p>You can therefore override the default location using the same technique as described above for Apache Isis' <code>isis.config.dir</code> context parameter. For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><Parameter</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">shiroConfigLocations</span><span class="delimiter">"</span></span>
<span class="attribute-name">value</span>=<span class="string"><span class="delimiter">"</span><span class="content">file:/usr/local/myapp/conf/shiro.ini</span><span class="delimiter">"</span></span>
<span class="attribute-name">override</span>=<span class="string"><span class="delimiter">"</span><span class="content">false</span><span class="delimiter">"</span></span> <span class="tag">/></span></code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Note that Shiro is more flexible than Apache Isis; it will read its configuration from any URL, not just a directory on the local filesystem.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_ugbtb_deployment_externalized-configuration_Log4j">8.3.3. Log4j Config</h4>
<div class="paragraph">
<p>By default Apache Isis configures log4j to read the <code>logging.properties</code> file in the <code>WEB-INF</code> directory. This can be overridden by setting the <code>log4j.properties</code> system property to the URL of the log4j properties file.</p>
</div>
<div class="paragraph">
<p>For example, if deploying to Tomcat7, this amounts to adding the following to the <code>CATALINA_OPTS</code> flags:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">export CATALINA_OPTS="-Dlog4j.configuration=/usr/local/tomcat/myapp/conf/logging.properties"</code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p><code>CATALINA_OPTS</code> was called <code>TOMCAT_OPTS</code> in earlier versions of Tomcat.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Further details an be found in the <a href="https://logging.apache.org/log4j/1.2/manual.html#Example_Configurations">log4j documentation</a>.</p>
</div>
</div>
<div class="sect3">
<h4 id="_spring_config">8.3.4. Spring Config</h4>
<div class="paragraph">
<p>Although Apache Isis does not use Spring, it’s possible that your app may use other components that do use Spring. For example, the (non-ASF) <a href="http://github.com/isisaddons/isis-module-publishmq">Isis addons' publishmq</a> module uses ActiveMQ and Camel to support publishing; both of these leverage Spring.</p>
</div>
<div class="paragraph">
<p>There are several ways to externalized Spring config, but the mechanism described here is similar in nature to those that we use for externalizing Apache Isis' and Shiro’s configuration. In your <code>web.xml</code>, you will probably load the Spring application context using code such as:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><listener></span>
<span class="tag"><listener-class></span>org.springframework.web.context.ContextLoaderListener<span class="tag"></listener-class></span>
<span class="tag"></listener></span>
<span class="tag"><context-param></span>
<span class="tag"><param-name></span>contextConfigLocation<span class="tag"></param-name></span>
<span class="tag"><param-value></span>
classpath:my-application-context-config.xml
<span class="tag"></param-value></span>
<span class="tag"></context-param></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Add a new application context <code>propertyPlaceholderConfigurer-config.xml</code> defining a <code>PropertyPlaceholderConfigurer</code> bean.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><beans</span>
<span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://www.springframework.org/schema/beans</span><span class="delimiter">"</span></span>
<span class="attribute-name">xmlns:xsi</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://www.w3.org/2001/XMLSchema-instance</span><span class="delimiter">"</span></span>
<span class="attribute-name">xsi:schemaLocation</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd</span>
<span class="content">http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd</span><span class="delimiter">"</span></span><span class="tag">></span>
<span class="tag"><bean</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">org.springframework.beans.factory.config.PropertyPlaceholderConfigurer</span><span class="delimiter">"</span></span><span class="tag">></span>
<span class="tag"><property</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">locations</span><span class="delimiter">"</span></span><span class="tag">></span>
<span class="tag"><list></span>
<span class="tag"><value></span>${spring.config.file}<span class="tag"></value></span>
<span class="tag"></list></span>
<span class="tag"></property></span>
<span class="tag"></bean></span>
<span class="tag"></beans></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>This reads the properties from a <code>spring.config.file</code>, defined as a context-param in the <code>web.xml</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><context-param></span>
<span class="tag"><param-name></span>spring.config.file<span class="tag"></param-name></span>
<span class="tag"><param-value></span>classpath:spring.properties<span class="tag"></param-value></span>
<span class="tag"></context-param></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Then update the bootstrapping in <code>web.xml</code> to use this new application context, eg:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><context-param></span>
<span class="tag"><param-name></span>contextConfigLocation<span class="tag"></param-name></span>
<span class="tag"><param-value></span>
classpath:my-application-context-config.xml,
classpath:propertyPlaceholderConfigurer-config.xml
<span class="tag"></param-value></span>
<span class="tag"></context-param></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>To use some other externalized configuration, override the <code>spring.config.file</code> property, eg using Tomcat’s config file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><Parameter</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">spring.config.dir</span><span class="delimiter">"</span></span>
<span class="attribute-name">value</span>=<span class="string"><span class="delimiter">"</span><span class="content">file:/usr/local/myapp/conf/spring.properties</span><span class="delimiter">"</span></span>
<span class="attribute-name">override</span>=<span class="string"><span class="delimiter">"</span><span class="content">false</span><span class="delimiter">"</span></span> <span class="tag">/></span></code></pre>
</div>
</div>
<div class="sect4">
<h5 id="_an_alternative_approach">An alternative approach</h5>
<div class="paragraph">
<p>As mentioned, there are several other ways to externalize Spring’s config; one approach is to use Spring’s profile support.</p>
</div>
<div class="paragraph">
<p>For example, in the application context you could have:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><beans</span> <span class="attribute-name">profile</span>=<span class="string"><span class="delimiter">"</span><span class="content">default</span><span class="delimiter">"</span></span><span class="tag">></span>
<span class="tag"><bean</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">org.springframework.beans.factory.config.PropertyPlaceholderConfigurer</span><span class="delimiter">"</span></span><span class="tag">></span>
<span class="tag"><property</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">locations</span><span class="delimiter">"</span></span><span class="tag">></span>
<span class="tag"><list></span>
<span class="tag"><value></span>classpath:dev.properties<span class="tag"></value></span>
<span class="tag"></list></span>
<span class="tag"></property></span>
<span class="tag"></bean></span>
<span class="tag"></beans></span>
<span class="tag"><beans</span> <span class="attribute-name">profile</span>=<span class="string"><span class="delimiter">"</span><span class="content">externalized</span><span class="delimiter">"</span></span><span class="tag">></span>
<span class="tag"><bean</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">propertyPlaceHolder</span><span class="delimiter">"</span></span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">org.springframework.beans.factory.config.PropertyPlaceholderConfigurer</span><span class="delimiter">"</span></span><span class="tag">></span>
<span class="tag"><property</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">locations</span><span class="delimiter">"</span></span><span class="tag">></span>
<span class="tag"><list></span>
<span class="tag"><value></span>classpath:prod.properties<span class="tag"></value></span>
<span class="tag"></list></span>
<span class="tag"></property></span>
<span class="tag"></bean></span>
<span class="tag"></beans></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The idea being that specifying the "prod" profile rather than the "default" profile would cause a different set of configuration properties to be read.</p>
</div>
<div class="paragraph">
<p>The active profile can be overridden with a system property, eg:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">-Dspring.active.profiles=prod</code></pre>
</div>
</div>
<div class="paragraph">
<p>take a look at <a href="http://stackoverflow.com/a/10041835/56880">this SO answer</a> on using Spring profiles.</p>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_see_also">8.3.5. See also</h4>
<div class="paragraph">
<p>See <a href="ugbtb.adoc#_ugbtb_deployment_jvm-flags">JVM args</a> for other JVM args and system properties that you might want to consider setting.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_deployment_docker">8.4. Docker</h3>
<div class="paragraph">
<p>When running the application within a Docker container, the problem that must be solved is to override the
configuration properties baked into the war file, eg to point to the app to a different JDBC URL.</p>
</div>
<div class="paragraph">
<p>There are several options.</p>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>All the options here rely on starting the Docker container with a set of arguments, some of which would very likely
be passwords for database connections etc. As such these techniques are only suitable where the security of the
Docker host can be assured.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_using_an_code_overrides_properties_code_file">8.4.1. Using an <code>overrides.properties</code> file</h4>
<div class="paragraph">
<p>In addition to loading the regular configuration properties from <code>WEB-INF</code> directory (described
<a href="rgcfg.html#_rgcfg_configuration-files">here</a>), Apache Isis will also load the <code>overrides.properties</code> file.</p>
</div>
<div class="paragraph">
<p>This file is treated slightly differently than the other configuration files; it is loaded last, and any configuration
properties defined in it will <em>override</em> any configuration properties already read from other files (this includes
any properties specified via the command line).</p>
</div>
<div class="paragraph">
<p>While the regular configuration files are "baked into" the application WAR file, the <code>overrides.properties</code> file is
created dynamically as part of the Docker <code>ENTRYPOINT</code> script, eg as documented in the
<a href="https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/">Dockerfile best practices</a>.</p>
</div>
<div class="paragraph">
<p>Thus, Docker can be supported as follows:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>use <code>mvn</code> (as currently) to create a WAR file; set up with the <code>pom.xml</code> with the JDBC drivers of all DB servers that
you might want to connect to (hsqldb, sql server, postgresql etc)</p>
</li>
<li>
<p>in the <code>Dockerfile</code>, specify a base image containing Tomcat 8 + Java 8 (say)</p>
</li>
<li>
<p>also in the <code>Dockerfile</code>, arrange it such that the WAR file is "exploded" (there is no need to copy over the WAR file itself)</p>
</li>
<li>
<p>write a script that:</p>
<div class="ulist">
<ul>
<li>
<p>explodes the WAR file, copying it into the Tomcat’s <code>webapp</code> directory. There is no need to copy
over the WAR file itself.</p>
</li>
<li>
<p>creates the <code>overrides.properties</code> file from any input arguments, placing it into the <code>WEB-INF</code> directory</p>
</li>
<li>
<p>sets all files to read-only</p>
</li>
</ul>
</div>
</li>
<li>
<p>use <code>ENTRYPOINT</code> (and probably also <code>CMD</code>) to invoke above script.</p>
</li>
</ul>
</div>
</div>
<div class="sect3">
<h4 id="_using_system_properties">8.4.2. Using system properties</h4>
<div class="paragraph">
<p>The servlet context initializer will search for any system properties called <code>isis.xxx</code>
and if present will use them as overrides.</p>
</div>
<div class="paragraph">
<p>Thus, an alternative option for a Docker image is to bootstrap the servlet container (Tomcat, Jetty) with appropriate
system properties set up. For example, with Tomcat this can be done by writing into the <code>conf/catalina.properties</code> file
(see for example <a href="http://stackoverflow.com/a/16566920">this stackoverflow</a> post).</p>
</div>
<div class="paragraph">
<p>The Docker’s <code>ENTRYPOINT</code> therefore just needs to parse the Docker container’s own command line arguments and use to
create this file.</p>
</div>
</div>
<div class="sect3">
<h4 id="_using_the_code_isis_opts_code_environment_variable">8.4.3. Using the <code>ISIS_OPTS</code> environment variable</h4>
<div class="paragraph">
<p>The servlet context initializer will search for an environment variable called <code>$ISIS_OPTS</code>
and if present will parse the content as a set of key/value pairs. Each key/value pair is separated by "||".</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">export ISIS_OPTS="isis.appManifest=domainapp.app.DomainAppAppManifestWithFixtures||isis.objects.editing=false"</code></pre>
</div>
</div>
<div class="paragraph">
<p>can be used to run with a different app manifest, and also to disable editing of properties.</p>
</div>
<div class="paragraph">
<p>To use a different separator, set the (optional) <code>$ISIS_OPTS_SEPARATOR</code> variable.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">export ISIS_OPTS_SEPARATOR=";"
export ISIS_OPTS="isis.appManifest=domainapp.app.DomainAppAppManifestWithFixtures;isis.objects.editing=false"</code></pre>
</div>
</div>
<div class="paragraph">
<p>The Docker’s <code>ENTRYPOINT</code> therefore just needs to parse the Docker container’s own command line arguments and use to
set this environment variable.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_deployment_gae">8.5. Deploying to Google App Engine</h3>
<div class="paragraph">
<p>The <a href="https://cloud.google.com/appengine/docs">Google App Engine</a> (GAE) provides a JDO API, meaning that you can deploy Apache Isis onto GAE using the JDO objectstore.</p>
</div>
<div class="paragraph">
<p>However, GAE is not an RDBMS, and so there are some limitations that it imposes. This page gathers together various hints, tips and workarounds.</p>
</div>
<div class="sect3">
<h4 id="_primary_keys_and_owned_unowned_relationships">8.5.1. Primary Keys and Owned/Unowned Relationships</h4>
<div class="paragraph">
<p>All entities must have a <code>@PrimaryKey</code>. Within GAE, the type of this key matters.</p>
</div>
<div class="paragraph">
<p>For an entity to be an aggregate root, (ie a root of an GAE entity group), its key must be a <code>Long</code>, eg:</p>
</div>
<div class="paragraph">
<p>Any collection that holds this entity type (eg <code>ToDoItem#dependencies</code> holding a collection of <code>ToDoItem`s) should then be annotated with `@Unowned</code> (a GAE annotation).</p>
</div>
<div class="paragraph">
<p>If on the other hand you want the object to be owned (through a 1:m relationship somewhere) by some other root, then use a String:</p>
</div>
<div class="paragraph">
<p>Note: if you store a relationship with a String key it means that the parent object <em>owns</em> the child, any attempt to change the relationship raise and exception.</p>
</div>
</div>
<div class="sect3">
<h4 id="_custom_types">8.5.2. Custom Types</h4>
<div class="paragraph">
<p>Currently Apache Isis' <code>Blob</code> and <code>Clob</code> types and the JODA types (<code>LocalDate</code> et al) are <em>not</em> supported in GAE.</p>
</div>
<div class="paragraph">
<p>Instead, GAE defines a <a href="https://cloud.google.com/appengine/docs/java/datastore/entities#Properties_and_Value_Types">fixed set of value types</a>, including <code>BlobKey</code>. Members of the Apache Isis community have this working, though I haven’t seen the code.</p>
</div>
<div class="paragraph">
<p>The above notwithstanding, Andy Jefferson at DataNucleus tells us:</p>
</div>
<div class="paragraph">
<p><div class="extended-quote-first"><p>GAE JDO/JPA does support <em>some</em> type conversion, because looking at <a href="http://code.google.com/p/datanucleus-appengine/source/browse/trunk/src/com/google/appengine/datanucleus/StoreFieldManager.java#349">StoreFieldManager.java</a> for any field that is Object-based and not a relation nor Serialized it will call <a href="http://code.google.com/p/datanucleus-appengine/source/browse/trunk/src/com/google/appengine/datanucleus/TypeConversionUtils.java#736">TypeConverstionUtils.java</a> and that looks for a <code>TypeConverter</code> (specify <code>@Extension</code> with key of "type-converter-name" against a field and value as the <code>TypeConverter</code> class) and it should convert it. Similarly when getting the value from the datastore.
</p></div></p>
</div>
<div class="paragraph">
<p>On further investigation, it seems that the GAE implementation performs a type check on a <code>SUPPORTED_TYPES</code> Java set, in <code>com.google.appengine.api.datastore.DataTypeUtils</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">if</span> (!supportedTypes.contains(value.getClass())) {
<span class="keyword">throw</span> <span class="keyword">new</span> <span class="exception">IllegalArgumentException</span>(prefix + value.getClass().getName() + <span class="string"><span class="delimiter">"</span><span class="content"> is not a supported property type.</span><span class="delimiter">"</span></span>);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>We still need to try out Andy’s recipe, above.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_deployment_neo4j">8.6. Neo4J</h3>
<div class="paragraph">
<p>As of 1.8.0 Apache Isis has experimental support for Neo4J, courtesy of DataNucleus' <a href="http://www.datanucleus.org/products/datanucleus/datastores/neo4j.html">Neo4J Datastore</a> implementation.</p>
</div>
<div class="paragraph">
<p>The <a href="ugfun.html#_ugfun_getting-started_simpleapp-archetype">SimpleApp archetype</a> has been updated so that they can be optionally run under Neo4J.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>In addition, the <a href="http://github.com/isisaddons/isis-app-neoapp">Isis addons' neoapp</a> (non-ASF) is configured to run with an embedded Neo4J server running alongside the Apache Isis webapp.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The steps below describe the configuration steps required to update an existing app.</p>
</div>
<div class="sect3">
<h4 id="_connectionurl">8.6.1. ConnectionURL</h4>
<div class="paragraph">
<p>In <code>persistor.properties</code>, update the JDO <code>ConnectionURL</code> property, eg:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="ini">isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionURL=neo4j:neo4j_DB</code></pre>
</div>
</div>
<div class="paragraph">
<p>The other connection properties (<code>ConnectionDriverName</code>, <code>ConnectionUserName</code> and <code>ConnectionPassword</code>) should be commented out.</p>
</div>
</div>
<div class="sect3">
<h4 id="_update_pom_xml">8.6.2. Update pom.xml</h4>
<div class="paragraph">
<p>Add the following dependency to the <code>webapp</code> project’s <code>pom.xml</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><dependencies></span>
...
<span class="tag"><dependency></span>
<span class="tag"><groupId></span>org.datanucleus<span class="tag"></groupId></span>
<span class="tag"><artifactId></span>datanucleus-neo4j<span class="tag"></artifactId></span>
<span class="tag"><version></span>4.0.5<span class="tag"></version></span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="tag"></dependency></span>
...
<span class="tag"></dependencies></span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>for Isis v1.9.0, use the value shown. for Isis v1.8.0, use 3.2.3.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>In the <a href="ugfun.html#_ugfun_getting-started_simpleapp-archetype">SimpleApp archetype</a> this is defined under the "neo4j" profile so can be activated using <code>-P neo4j</code>.</p>
</div>
</div>
<div class="sect3">
<h4 id="_try_it_out">8.6.3. Try it out!</h4>
<div class="paragraph">
<p>If you want to quickly try out neo4j for yourself:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>run the <a href="ugfun.html#_ugfun_getting-started_simpleapp-archetype">SimpleApp archetype</a> (v1.8.0)</p>
</li>
<li>
<p>build the app:</p>
</li>
<li>
<p>run the app:</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>If you visit the about page you should see the neo4j JAR files are linked in, and a <code>neo4j_DB</code> subdirectory within the <code>webapp</code> directory.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_deployment_jvm-flags">8.7. JVM Flags</h3>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
TODO
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The default JVM configuration will most likely not be appropriate for running Isis as a webapp. The table below suggests some JVM args that you will probably want to modify:</p>
</div>
<table class="tableblock frame-all grid-all spread">
<caption class="title">Table 3. JVM args</caption>
<colgroup>
<col style="width: 50%;">
<col style="width: 50%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Flag</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">-server</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Run the JVM in server mode, meaning that the JVM should spend more time on the optimization of the fragments of codes that are most often used (hotspots). This leads to better performance at the price of a higher overhead at startup.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">-Xms128m
Minimum heap size</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">-Xmx768m</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Maximum heap size</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">-XX:PermSize=64m</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Minimum perm size (for class definitions)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">-XX:MaxPermSize=256m</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Maximum perm size (for class definitions)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">-XX:+DisableExplicitGC</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>There are also a whole bunch of GC-related flags, that you might want to explore; see this detailed <a href="http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html">Hotspot JVM</a> documentation and also this <a href="http://blog.ragozin.info/2011/09/hotspot-jvm-garbage-collection-options.html">blog post</a>.</p>
</div>
<div class="sect3">
<h4 id="_configuring_in_tomcat">8.7.1. Configuring in Tomcat</h4>
<div class="paragraph">
<p>If using Tomcat, update the <code>CATALINA_OPTS</code> variable. (This variable is also updated if <a href="#_ugbtb_deployment_externalized-configuration_Log4j">configuring logging to run externally</a>).</p>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_ugbtb_web-xml">9. <code>web.xml</code></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Apache Isis provides two different viewers, the <a href="ugvw.html">Wicket viewer</a> and the <a href="ugvro.html">RestfulObjects viewer</a>. You can deploy both of these concurrently, or deploy just the Wicket viewer, or deploy just the Restful Objects viewer. The configuration in <code>web.xml</code> varies accordingly, both in terms of the servlet context listeners, filters and servlets.</p>
</div>
<div class="paragraph">
<p>If you are using Apache Isis' integration with Apache Shiro (for security) then this also needs configuring in <code>web.xml</code>. See the <a href="ugsec.html#_ugsec_configuring-isis-to-use-shiro">security chapter</a> for full details on this topic.</p>
</div>
<div class="paragraph">
<p>The servlets and filters are mapped to three main pipelines:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>/wicket</code> - the Wicket viewer UI</p>
</li>
<li>
<p><code>/restful</code> - the Restful Objects resources (REST API)</p>
</li>
<li>
<p>other paths, also static resources (such as <code>.png</code>, <code>.css</code>)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>With the following key:</p>
</div>
<div class="imageblock">
<div class="content">
<a class="image" href="images/runtime/web-xml/key.png"><img src="images/runtime/web-xml/key.png" alt="key" width="800px"></a>
</div>
</div>
<div class="paragraph">
<p>the diagram below shows the components to be configured if deploying both the Wicket viewer and Restful Objects viewer:</p>
</div>
<div class="imageblock">
<div class="content">
<a class="image" href="images/runtime/web-xml/wicket-and-ro.png"><img src="images/runtime/web-xml/wicket-and-ro.png" alt="wicket and ro" width="800px"></a>
</div>
</div>
<div class="paragraph">
<p>Here the Wicket viewer is responsible for the main bootstrapping of Apache Isis itself, in other words the shared (global) metadata; this is done by the <code>IsisWicketApplication</code> class (extending the <code>WicketApplication</code> Wicket API). This class is also responsible for Apache Isis' own session and transaction management.</p>
</div>
<div class="paragraph">
<p>The Restful Objects viewer - being a JAX-RS application implemented using the RestEasy framework - requires the <code>RestEasyBootstrapper</code> servlet context listener. It is this context listener that also sets up the <code>RestfulObjectsApplication</code>, which is then delegated to by the RestEasy <code>HttpServletDispatcher</code>. This pipeline uses the <code>IsisSessionFilter</code> and <code>IsisTransactionFilterForRestfulObjects</code> to perform the session and transaction management before it hits the RestEasy servlet.</p>
</div>
<div class="paragraph">
<p>If only the Wicket viewer is deployed, then the diagram is more or less the same: the RestEasy servlet, context listener and supporting filters are simply removed:</p>
</div>
<div class="imageblock">
<div class="content">
<a class="image" href="images/runtime/web-xml/wicket-only.png"><img src="images/runtime/web-xml/wicket-only.png" alt="wicket only" width="800px"></a>
</div>
</div>
<div class="paragraph">
<p>Finally, if only the Restful Objects viewer is deployed, then things change a little more subtly. Here, the Wicket filter is no longer needed. In its place, though the <code>IsisWebAppBootstrapper</code> context listener is required: this is responsible for seting up the shared (global) metadata.</p>
</div>
<div class="imageblock">
<div class="content">
<a class="image" href="images/runtime/web-xml/ro-only.png"><img src="images/runtime/web-xml/ro-only.png" alt="ro only" width="800px"></a>
</div>
</div>
<div class="paragraph">
<p>The following sections detail these various listeners, filters and servlets in more detail.</p>
</div>
<div class="sect2">
<h3 id="_ugbtb_web-xml_servlet-context-listeners">9.1. Servlet Context Listeners</h3>
<div class="paragraph">
<p>Servlet context listeners are used to perform initialization on application startup. Both Shiro (if configured as the security mechanism) and RestEasy (for the Restful Objects viewer) require their own context listener. In addition, if the Wicket viewer is <em>not</em> being used, then additional Apache Isis-specific listener is required for bootstrapping of the Apache Isis framework itself.</p>
</div>
<div class="sect3">
<h4 id="__code_environmentloaderlistener_code_shiro">9.1.1. <code>EnvironmentLoaderListener</code> (Shiro)</h4>
<div class="paragraph">
<p>Bootstrap listener to startup and shutdown the web application’s Shiro <code>WebEnvironment</code> at startup and shutdown respectively.</p>
</div>
<div class="paragraph">
<p>Its definition is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><listener></span>
<span class="tag"><listener-class></span>org.apache.shiro.web.env.EnvironmentLoaderListener<span class="tag"></listener-class></span>
<span class="tag"></listener></span></code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="__code_isiswebappbootstrapper_code">9.1.2. <code>IsisWebAppBootstrapper</code></h4>
<div class="paragraph">
<p>The <code>IsisWebAppBootstrapper</code> servlet context listener bootstraps the shared (global) metadata for the Apache Isis framework. This listener is not required (indeed must not be configured) if the Wicket viewer is in use.</p>
</div>
<div class="paragraph">
<p>Its definition is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><listener></span>
<span class="tag"><listener-class></span>org.apache.isis.core.webapp.IsisWebAppBootstrapper<span class="tag"></listener-class></span>
<span class="tag"></listener></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Its context parameters are:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><context-param></span>
<span class="tag"><param-name></span>deploymentType<span class="tag"></param-name></span>
<span class="tag"><param-value></span>SERVER_PROTOTYPE<span class="tag"></param-value></span>
<span class="tag"></context-param></span>
<span class="tag"><context-param></span>
<span class="tag"><param-name></span>isis.viewers<span class="tag"></param-name></span>
<span class="tag"><param-value></span>restfulobjects<span class="tag"></param-value></span>
<span class="tag"></context-param></span></code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="__code_resteasybootstrap_code_resteasy">9.1.3. <code>ResteasyBootstrap</code> (RestEasy)</h4>
<div class="paragraph">
<p>The <code>ResteasyBootstrap</code> servlet context listener initializes the RestEasy runtime, specifying that classes (namely, those specified in Isis' <code>RestfulObjectsApplication</code>) to be exposed as REST resources. It is required if the Restful Objects viewer is to be deployed.</p>
</div>
<div class="paragraph">
<p>Its definition is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><listener></span>
<span class="tag"><listener-class></span>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap<span class="tag"></listener-class></span>
<span class="tag"></listener></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>There are two relevant context parameters:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><context-param></span>
<span class="tag"><param-name></span>javax.ws.rs.Application<span class="tag"></param-name></span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="tag"><param-value></span>org.apache.isis.viewer.restfulobjects.server.RestfulObjectsApplication<span class="tag"></param-value></span>
<span class="tag"></context-param></span>
<span class="tag"><context-param></span>
<span class="tag"><param-name></span>resteasy.servlet.mapping.prefix<span class="tag"></param-name></span>
<span class="tag"><param-value></span>/restful/<span class="tag"></param-value></span> <i class="conum" data-value="2"></i><b>(2)</b>
<span class="tag"></context-param></span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>used by RestEasy to determine the JAX-RS resources and other related configuration</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>should correspond to the filter mapping of the <code>HttpServletDispatcher</code> servlet</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_web-xml_servlets">9.2. Servlets</h3>
<div class="paragraph">
<p>Servlets process HTTP requests and return corresponding responses.</p>
</div>
<div class="sect3">
<h4 id="__code_httpservletdispatcher_code_resteasy">9.2.1. <code>HttpServletDispatcher</code> (RestEasy)</h4>
<div class="paragraph">
<p>This servlet is provided by the RestEasy framework, and does the dispatching to the resources defined by Apache Isis' <code>RestfulObjectsApplication</code> (see above).</p>
</div>
<div class="paragraph">
<p>Its definition is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><servlet></span>
<span class="tag"><servlet-name></span>RestfulObjectsRestEasyDispatcher<span class="tag"></servlet-name></span>
<span class="tag"><servlet-class></span>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher<span class="tag"></servlet-class></span>
<span class="tag"></servlet></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Its mapping is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><servlet-mapping></span>
<span class="tag"><servlet-name></span>RestfulObjectsRestEasyDispatcher<span class="tag"></servlet-name></span>
<span class="tag"><url-pattern></span>/restful/*<span class="tag"></url-pattern></span>
<span class="tag"></servlet-mapping></span></code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="__code_resourceservlet_code">9.2.2. <code>ResourceServlet</code></h4>
<div class="paragraph">
<p>The <code>ResourceServlet</code> loads and services static content either from the filesystem or from the classpath, each with an appropriate mime type.</p>
</div>
<div class="paragraph">
<p>Static content here means request paths ending in <code>.js</code>, <code>.css</code>, <code>.html</code>, <code>.png</code>, <code>.jpg</code>, <code>.jpeg</code> and <code>gif</code>.</p>
</div>
<div class="paragraph">
<p>Its definition is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><servlet></span>
<span class="tag"><servlet-name></span>Resource<span class="tag"></servlet-name></span>
<span class="tag"><servlet-class></span>org.apache.isis.core.webapp.content.ResourceServlet<span class="tag"></servlet-class></span>
<span class="tag"></servlet></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Its mapping is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><servlet-mapping></span>
<span class="tag"><servlet-name></span>Resource<span class="tag"></servlet-name></span>
<span class="tag"><url-pattern></span>*.css<span class="tag"></url-pattern></span>
<span class="tag"></servlet-mapping></span>
<span class="tag"><servlet-mapping></span>
<span class="tag"><servlet-name></span>Resource<span class="tag"></servlet-name></span>
<span class="tag"><url-pattern></span>*.png<span class="tag"></url-pattern></span>
<span class="tag"></servlet-mapping></span>
<span class="tag"><servlet-mapping></span>
<span class="tag"><servlet-name></span>Resource<span class="tag"></servlet-name></span>
<span class="tag"><url-pattern></span>*.jpg<span class="tag"></url-pattern></span>
<span class="tag"></servlet-mapping></span>
<span class="tag"><servlet-mapping></span>
<span class="tag"><servlet-name></span>Resource<span class="tag"></servlet-name></span>
<span class="tag"><url-pattern></span>*.jpeg<span class="tag"></url-pattern></span>
<span class="tag"></servlet-mapping></span>
<span class="tag"><servlet-mapping></span>
<span class="tag"><servlet-name></span>Resource<span class="tag"></servlet-name></span>
<span class="tag"><url-pattern></span>*.gif<span class="tag"></url-pattern></span>
<span class="tag"></servlet-mapping></span>
<span class="tag"><servlet-mapping></span>
<span class="tag"><servlet-name></span>Resource<span class="tag"></servlet-name></span>
<span class="tag"><url-pattern></span>*.svg<span class="tag"></url-pattern></span>
<span class="tag"></servlet-mapping></span>
<span class="tag"><servlet-mapping></span>
<span class="tag"><servlet-name></span>Resource<span class="tag"></servlet-name></span>
<span class="tag"><url-pattern></span>*.js<span class="tag"></url-pattern></span>
<span class="tag"></servlet-mapping></span>
<span class="tag"><servlet-mapping></span>
<span class="tag"><servlet-name></span>Resource<span class="tag"></servlet-name></span>
<span class="tag"><url-pattern></span>*.html<span class="tag"></url-pattern></span>
<span class="tag"></servlet-mapping></span>
<span class="tag"><servlet-mapping></span>
<span class="tag"><servlet-name></span>Resource<span class="tag"></servlet-name></span>
<span class="tag"><url-pattern></span>*.swf<span class="tag"></url-pattern></span>
<span class="tag"></servlet-mapping></span></code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_web-xml_filters">9.3. Filters</h3>
<div class="paragraph">
<p>The order in which filters appear in <code>web.xml</code> matters: first to last they define a pipeline. This is shown in the
above diagrams, and the subsections also list the in the same order that they should appear in your <code>web.xml</code>.</p>
</div>
<div class="sect3">
<h4 id="__code_shirofilter_code_shiro">9.3.1. <code>ShiroFilter</code> (Shiro)</h4>
<div class="paragraph">
<p>Shiro filter that sets up a Shiro security manager for the request, obtained from the Shiro <code>WebEnvironment</code> set up
by the Shiro <code>EnvironmentLoaderListener</code> (discussed above).</p>
</div>
<div class="paragraph">
<p>Its definition is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><filter></span>
<span class="tag"><filter-name></span>ShiroFilter<span class="tag"></filter-name></span>
<span class="tag"><filter-class></span>org.apache.shiro.web.servlet.ShiroFilter<span class="tag"></filter-class></span>
<span class="tag"></filter></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Its mapping is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><filter-mapping></span>
<span class="tag"><filter-name></span>ShiroFilter<span class="tag"></filter-name></span>
<span class="tag"><url-pattern></span>/*<span class="tag"></url-pattern></span>
<span class="tag"></filter-mapping></span></code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="__code_isislogonexceptionfilter_code">9.3.2. <code>IsisLogOnExceptionFilter</code></h4>
<div class="paragraph">
<p>The <code>IsisLogOnExceptionFilter</code> filter simply logs the URL of any request that causes an exception to be thrown, then
re-propagates the exception. The use case is simply to ensure that all exceptions are logged (against the
<code>IsisLogOnExceptionFilter</code> slf4j appender).</p>
</div>
<div class="paragraph">
<p>Its definition is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><filter></span>
<span class="tag"><filter-name></span>IsisLogOnExceptionFilter<span class="tag"></filter-name></span>
<span class="tag"><filter-class></span>org.apache.isis.core.webapp.diagnostics.IsisLogOnExceptionFilter<span class="tag"></filter-class></span>
<span class="tag"></filter></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Its mapping is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><filter-mapping></span>
<span class="tag"><filter-name></span>IsisLogOnExceptionFilter<span class="tag"></filter-name></span>
<span class="tag"><url-pattern></span>/wicket/*<span class="tag"></url-pattern></span>
<span class="tag"></filter-mapping></span>
<span class="tag"><filter-mapping></span>
<span class="tag"><filter-name></span>IsisLogOnExceptionFilter<span class="tag"></filter-name></span>
<span class="tag"><url-pattern></span>/restful/*<span class="tag"></url-pattern></span>
<span class="tag"></filter-mapping></span></code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="__code_resourcecachingfilter_code">9.3.3. <code>ResourceCachingFilter</code></h4>
<div class="paragraph">
<p>The <code>ResourceCachingFilter</code> adds HTTP cache headers to specified resources, based on their pattern.</p>
</div>
<div class="paragraph">
<p>Its definition is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><filter></span>
<span class="tag"><filter-name></span>ResourceCachingFilter<span class="tag"></filter-name></span>
<span class="tag"><filter-class></span>org.apache.isis.core.webapp.content.ResourceCachingFilter<span class="tag"></filter-class></span>
<span class="tag"><init-param></span>
<span class="tag"><param-name></span>CacheTime<span class="tag"></param-name></span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="tag"><param-value></span>86400<span class="tag"></param-value></span>
<span class="tag"></init-param></span>
<span class="tag"></filter></span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>cache time, in seconds</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Its mapping is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><filter-mapping></span>
<span class="tag"><filter-name></span>ResourceCachingFilter<span class="tag"></filter-name></span>
<span class="tag"><url-pattern></span>*.css<span class="tag"></url-pattern></span>
<span class="tag"></filter-mapping></span>
<span class="tag"><filter-mapping></span>
<span class="tag"><filter-name></span>ResourceCachingFilter<span class="tag"></filter-name></span>
<span class="tag"><url-pattern></span>*.png<span class="tag"></url-pattern></span>
<span class="tag"></filter-mapping></span>
<span class="tag"><filter-mapping></span>
<span class="tag"><filter-name></span>ResourceCachingFilter<span class="tag"></filter-name></span>
<span class="tag"><url-pattern></span>*.jpg<span class="tag"></url-pattern></span>
<span class="tag"></filter-mapping></span>
<span class="tag"><filter-mapping></span>
<span class="tag"><filter-name></span>ResourceCachingFilter<span class="tag"></filter-name></span>
<span class="tag"><url-pattern></span>*.jpeg<span class="tag"></url-pattern></span>
<span class="tag"></filter-mapping></span>
<span class="tag"><filter-mapping></span>
<span class="tag"><filter-name></span>ResourceCachingFilter<span class="tag"></filter-name></span>
<span class="tag"><url-pattern></span>*.gif<span class="tag"></url-pattern></span>
<span class="tag"></filter-mapping></span>
<span class="tag"><filter-mapping></span>
<span class="tag"><filter-name></span>ResourceCachingFilter<span class="tag"></filter-name></span>
<span class="tag"><url-pattern></span>*.svg<span class="tag"></url-pattern></span>
<span class="tag"></filter-mapping></span>
<span class="tag"><filter-mapping></span>
<span class="tag"><filter-name></span>ResourceCachingFilter<span class="tag"></filter-name></span>
<span class="tag"><url-pattern></span>*.html<span class="tag"></url-pattern></span>
<span class="tag"></filter-mapping></span>
<span class="tag"><filter-mapping></span>
<span class="tag"><filter-name></span>ResourceCachingFilter<span class="tag"></filter-name></span>
<span class="tag"><url-pattern></span>*.js<span class="tag"></url-pattern></span>
<span class="tag"></filter-mapping></span>
<span class="tag"><filter-mapping></span>
<span class="tag"><filter-name></span>ResourceCachingFilter<span class="tag"></filter-name></span>
<span class="tag"><url-pattern></span>*.swf<span class="tag"></url-pattern></span>
<span class="tag"></filter-mapping></span></code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="__code_wicketfilter_code">9.3.4. <code>WicketFilter</code></h4>
<div class="paragraph">
<p>The <code>WicketFilter</code> is responsible for initiating the handling of Wicket requests.</p>
</div>
<div class="paragraph">
<p>Its definition is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><filter></span>
<span class="tag"><filter-name></span>WicketFilter<span class="tag"></filter-name></span>
<span class="tag"><filter-class></span>org.apache.wicket.protocol.http.WicketFilter<span class="tag"></filter-class></span>
<span class="tag"><init-param></span>
<span class="tag"><param-name></span>applicationClassName<span class="tag"></param-name></span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="tag"><param-value></span>domainapp.webapp.SimpleApplication<span class="tag"></param-value></span>
<span class="tag"></init-param></span>
<span class="tag"></filter></span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>specify the application (subclass of <code>IsisWicketApplication</code>) to use</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Its mapping is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><filter-mapping></span>
<span class="tag"><filter-name></span>WicketFilter<span class="tag"></filter-name></span>
<span class="tag"><url-pattern></span>/wicket/*<span class="tag"></url-pattern></span>
<span class="tag"></filter-mapping></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>This filter reads one context parameter:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><context-param></span>
<span class="tag"><param-name></span>configuration<span class="tag"></param-name></span>
<span class="tag"><param-value></span>deployment<span class="tag"></param-value></span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="tag"></context-param></span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>alternatively set to "development"; see <a href="rgcfg.html#_rgcfg_deployment-types">deployment types</a> for further discussion.</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="__code_isissessionfilter_code">9.3.5. <code>IsisSessionFilter</code></h4>
<div class="paragraph">
<p>The <code>IsisSessionFilter</code> is responsible for the (persistence) session management; in effect a wrapper around DataNucleus' <code>PersistenceManager</code> object. It is only required for the Restful Objects viewer.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><filter></span>
<span class="tag"><filter-name></span>IsisSessionFilterForRestfulObjects<span class="tag"></filter-name></span>
<span class="tag"><filter-class></span>org.apache.isis.core.webapp.IsisSessionFilter<span class="tag"></filter-class></span>
<span class="tag"><init-param></span>
<span class="tag"><param-name></span>authenticationSessionStrategy<span class="tag"></param-name></span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="tag"><param-value></span>
org.apache.isis.viewer.restfulobjects.server.authentication.AuthenticationSessionStrategyBasicAuth
<span class="tag"></param-value></span>
<span class="tag"></init-param></span>
<span class="tag"><init-param></span>
<span class="tag"><param-name></span>whenNoSession<span class="tag"></param-name></span> <i class="conum" data-value="2"></i><b>(2)</b>
<span class="tag"><param-value></span>basicAuthChallenge<span class="tag"></param-value></span>
<span class="tag"></init-param></span>
<span class="tag"><init-param></span>
<span class="tag"><param-name></span>passThru<span class="tag"></param-name></span> <i class="conum" data-value="3"></i><b>(3)</b>
<span class="tag"><param-value></span>/restful/swagger<span class="tag"></param-value></span>
<span class="tag"></init-param></span>
<span class="comment"><!--
<init-param>
<param-name>restricted</param-name> <i class="conum" data-value="4"></i><b>(4)</b>
<param-value>...</param-value>
</init-param>
<init-param>
<param-name>redirectToOnException</param-name> <i class="conum" data-value="5"></i><b>(5)</b>
<param-value>...</param-value>
</init-param>
--></span>
<span class="tag"></filter></span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>pluggable strategy for determining what the authentication session (credentials) are of the request</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>what the servlet should do if no existing session was found. Usual values are either <code>unauthorized</code>, <code>basicAuthChallenge</code> or <code>auto</code>. Discussed in more detail below.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>specify which URIs to ignore and simply passthru. Originally introduced to allow the <code>SwaggerSpec</code> resource (which does not require a session) to be invoked.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>List of paths that are allowed through even if not authenticated. The servlets mapped to these paths are expected to be able to deal with there being no session. Typically they will be logon pages. See below for further details.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>where to redirect to if an exception occurs.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The <code>whenNoSession</code> parameter determines what the behaviour should be if no existing session can be found. There are a number of predetermined values available:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>unauthorized</code> will generates a 401 response</p>
</li>
<li>
<p><code>basicAuthChallenge</code> will also generate a 401 response, and also issues a Basic Auth challenge using <code>WWW-Authenticate</code> response header</p>
</li>
<li>
<p><code>auto</code> combines the <code>unauthorized</code> and <code>basicAuthChallenge</code> strategies: it will generate a 401 response, but only issues a Basic Auth challenge if it detects that the request originates from a web browser (ie that the HTTP <code>Accept</code> header is set to <code>text/html</code>). This means that custom Javascript apps can perform their authentication correctly, while the REST API can still be explored using the web browser (relying upon the web browser’s in-built support for HTTP Basic Auth).</p>
</li>
<li>
<p><code>continue</code>, in which case the request is allowed to continue but the destination expected to know that there will be no open session</p>
</li>
<li>
<p><code>restricted</code>, which allows access to a restricted list of URLs, otherwise will redirect to the first of that list of URLs</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>If accessing the REST API through a web browser, then normally <code>basicAuthChallenge</code> is appropriate; the browser will automatically display a simple prompt. If accessing the REST API through a custom Javascript app, then <code>unauthorized</code> is usually the one to use.</p>
</div>
<div class="paragraph">
<p>This filter should be mapped to the <code>servlet-name</code> for the RestEasy <code>HttpServletDispatcher</code>; for example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><filter-mapping></span>
<span class="tag"><filter-name></span>IsisSessionFilterForRestfulObjects<span class="tag"></filter-name></span>
<span class="tag"><servlet-name></span>RestfulObjectsRestEasyDispatcher<span class="tag"></servlet-name></span>
<span class="tag"></filter-mapping></span></code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="__code_isistransactionfilterforrestfulobjects_code">9.3.6. <code>IsisTransactionFilterForRestfulObjects</code></h4>
<div class="paragraph">
<p>The <code>IsisTransactionFilterForRestfulObjects</code> filter simply ensures that a transaction is in progress for all
calls routed to the <a href="ugvro.html">RestfulObjects viewer</a>.</p>
</div>
<div class="paragraph">
<p>Its definition is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><filter></span>
<span class="tag"><filter-name></span>IsisTransactionFilterForRestfulObjects<span class="tag"></filter-name></span>
<span class="tag"><filter-class></span>org.apache.isis.viewer.restfulobjects.server.webapp.IsisTransactionFilterForRestfulObjects<span class="tag"></filter-class></span>
<span class="tag"></filter></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>This filter should be mapped to the <code>servlet-name</code> for the RestEasy <code>HttpServletDispatcher</code>; for example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><filter-mapping></span>
<span class="tag"><filter-name></span>IsisTransactionFilterForRestfulObjects<span class="tag"></filter-name></span>
<span class="tag"><servlet-name></span>RestfulObjectsRestEasyDispatcher<span class="tag"></servlet-name></span>
<span class="tag"></filter-mapping></span></code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_ugbtb_web-xml_context-parameters">9.4. Configuration Files</h3>
<div class="paragraph">
<p>However Apache Isis is bootstrapped (using the <code>IsisWicketApplication</code> or using <code>IsisWebAppBootstrapper</code>), it will
read a number of configuration files, such as <code>isis.properties</code>.</p>
</div>
<div class="paragraph">
<p>By default these are read from <code>WEB-INF</code> directory. This can be overriden using the <code>isis.config.dir</code> context parameter:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><context-param></span>
<span class="tag"><param-name></span>isis.config.dir<span class="tag"></param-name></span>
<span class="tag"><param-value></span>location of your config directory if fixed<span class="tag"></param-value></span>
<span class="tag"></context-param></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Another context parameter, <code>isis.viewres</code> specifies which additional configuration files to search for (over and above
the default ones of <code>isis.properties</code> et al):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><context-param></span>
<span class="tag"><param-name></span>isis.viewers<span class="tag"></param-name></span>
<span class="tag"><param-value></span>wicket,restfulobjects<span class="tag"></param-value></span>
<span class="tag"></context-param></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>For example, this will cause <code>viewer_wicket.properties</code> and <code>viewer_restfulobjects.properties</code> to also be loaded.</p>
</div>
</div>
</div>
</div>
</div>
<footer>
<hr>
<p class="small">
Copyright © 2010~2016 The Apache Software Foundation, licensed under the Apache License, v2.0.
<br/>
Apache, the Apache feather logo, Apache Isis, and the Apache Isis project logo are all trademarks of The Apache Software Foundation.
</p>
</footer>
</div>
<div id="doc-content-right" class="large-3 medium-3 xcolumns">
<div id="toc" class="toc2">
<div class="fallback-toc">
<ul class="sectlevel1">
<li><a href="#_ugbtb">1. Beyond the Basics</a>
<ul class="sectlevel2">
<li><a href="#_other_guides">1.1. Other Guides</a></li>
</ul>
</li>
<li><a href="#_ugbtb_view-models">2. View Models</a>
<ul class="sectlevel2">
<li><a href="#_ugbtb_view-models_use-cases">2.1. Use Cases</a>
<ul class="sectlevel3">
<li><a href="#_ugbtb_view-models_use-cases_externally-managed-entities">2.1.1. Externally-managed entities</a></li>
<li><a href="#_ugbtb_view-models_use-cases_in-memory-entities">2.1.2. In-memory entities</a></li>
<li><a href="#_ugbtb_view-models_use-cases_application-layer-view-models">2.1.3. Application-layer view models</a></li>
<li><a href="#_ugbtb_view-models_use-cases_dtos">2.1.4. DTOs</a>
<ul class="sectlevel4">
<li><a href="#_ugbtb_view-models_use-cases_dtos_consumers">DTO Consumers</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#_ugbtb_view-models_programming-model">2.2. Programming Model</a></li>
<li><a href="#_ugbtb_view-models_jaxb">2.3. JAXB-annotated DTOs</a>
<ul class="sectlevel3">
<li><a href="#_ugbtb_view-models_jaxb_referencing-domain-entities">2.3.1. Referencing Domain Entities</a></li>
<li><a href="#_ugbtb_view-models_jaxb_versioning">2.3.2. Versioning</a></li>
<li><a href="#_ugbtb_view-models_jaxb_generating-xsds-and-dtos">2.3.3. Generating XSDs and DTOs</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#_ugbtb_decoupling">3. Decoupling</a>
<ul class="sectlevel2">
<li><a href="#_ugbtb_decoupling_db-schemas">3.1. Database Schemas</a>
<ul class="sectlevel3">
<li><a href="#_listener_to_create_schema">3.1.1. Listener to create schema</a></li>
<li><a href="#_alternative_implementation">3.1.2. Alternative implementation</a></li>
</ul>
</li>
<li><a href="#_ugbtb_decoupling_mixins">3.2. Mixins</a>
<ul class="sectlevel3">
<li><a href="#_example">3.2.1. Example</a></li>
<li><a href="#_contributing_a_single_member">3.2.2. Contributing a single member</a></li>
<li><a href="#_programmatic_usage">3.2.3. Programmatic usage</a></li>
<li><a href="#_other_reasons_to_use_mixins">3.2.4. Other reasons to use mixins</a></li>
<li><a href="#_related_reading">3.2.5. Related reading</a></li>
</ul>
</li>
<li><a href="#_ugbtb_decoupling_contributions">3.3. Contributions</a>
<ul class="sectlevel3">
<li><a href="#_syntax">3.3.1. Syntax</a></li>
<li><a href="#_contributed_action">3.3.2. Contributed Action</a></li>
<li><a href="#_contributed_property">3.3.3. Contributed Property</a></li>
<li><a href="#_contributed_collection">3.3.4. Contributed Collection</a></li>
</ul>
</li>
<li><a href="#_ugbtb_decoupling_vetoing-visibility">3.4. Vetoing Visibility</a></li>
<li><a href="#_ugbtb_decoupling_event-bus">3.5. Event Bus</a></li>
<li><a href="#_ugbtb_decoupling_pushing-changes">3.6. Pushing Changes</a>
<ul class="sectlevel3">
<li><a href="#_when_a_property_is_changed">3.6.1. When a property is changed</a></li>
<li><a href="#_when_a_collection_is_modified">3.6.2. When a collection is modified</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#_ugbtb_i18n">4. i18n</a>
<ul class="sectlevel2">
<li><a href="#_ugbtb_i18n_implementation-approach">4.1. Implementation Approach</a></li>
<li><a href="#_ugbtb_i18n_translation-service">4.2. <code>TranslationService</code></a>
<ul class="sectlevel3">
<li><a href="#__code_translationservicepo_code">4.2.1. <code>TranslationServicePo</code></a></li>
</ul>
</li>
<li><a href="#_ugbtb_i18n_imperative-messages">4.3. Imperative messages</a>
<ul class="sectlevel3">
<li><a href="#__code_translatablestring_code">4.3.1. <code>TranslatableString</code></a></li>
<li><a href="#__code_translatableexception_code">4.3.2. <code>TranslatableException</code></a></li>
</ul>
</li>
<li><a href="#_ugbtb_i18n_wicket-viewer">4.4. Wicket Viewer</a>
<ul class="sectlevel3">
<li><a href="#_commonly_used">4.4.1. Commonly used</a></li>
<li><a href="#_login_self_sign_up">4.4.2. Login/self-sign-up</a></li>
</ul>
</li>
<li><a href="#_ugbtb_i18n_integration-testing">4.5. Integration Testing</a></li>
<li><a href="#_escaped_strings">4.6. Escaped strings</a></li>
<li><a href="#_ugbtb_i18n_configuration">4.7. Configuration</a>
<ul class="sectlevel3">
<li><a href="#_logging">4.7.1. Logging</a></li>
<li><a href="#_location_of_the_code_po_code_files">4.7.2. Location of the <code>.po</code> files</a></li>
<li><a href="#_externalized_translation_files">4.7.3. Externalized translation files</a></li>
<li><a href="#_force_read_mode">4.7.4. Force read mode</a></li>
</ul>
</li>
<li><a href="#_supporting_services">4.8. Supporting services</a>
<ul class="sectlevel3">
<li><a href="#__code_localeprovider_code">4.8.1. <code>LocaleProvider</code></a></li>
<li><a href="#__code_translationsresolver_code">4.8.2. <code>TranslationsResolver</code></a></li>
<li><a href="#__code_translationservicepomenu_code">4.8.3. <code>TranslationServicePoMenu</code></a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#_ugbtb_headless-access">5. Headless access</a>
<ul class="sectlevel2">
<li><a href="#_ugbtb_headless-access_AbstractIsisSessionTemplate">5.1. AbstractIsisSessionTemplate</a></li>
<li><a href="#_ugbtb_headless-access_BackgroundCommandExecution">5.2. BackgroundCommandExecution</a>
<ul class="sectlevel3">
<li><a href="#_background_execution">5.2.1. Background Execution</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#_ugbtb_other-techniques">6. Other Techniques</a>
<ul class="sectlevel2">
<li><a href="#_ugbtb_other-techniques_mapping-rdbms-views">6.1. Mapping RDBMS Views</a></li>
<li><a href="#_ugbtb_other-techniques_transactions-and-errors">6.2. Transactions and Errors</a>
<ul class="sectlevel3">
<li><a href="#_raise_message_errors_to_users">6.2.1. Raise message/errors to users</a></li>
<li><a href="#_aborting_transactions">6.2.2. Aborting transactions</a></li>
</ul>
</li>
<li><a href="#_ugbtb_other-techniques_multi-tenancy">6.3. Multi-tenancy</a></li>
<li><a href="#_ugbtb_other-techniques_persisted-title">6.4. Persisted Title</a></li>
<li><a href="#_ugbtb_other-techniques_replacing-default-service-implementations">6.5. Overriding Default Service Implns</a></li>
</ul>
</li>
<li><a href="#_ugbtb_programming-model">7. Customizing the Prog Model</a>
<ul class="sectlevel2">
<li><a href="#_ugbtb_programming-model_custom-validator">7.1. Custom validator</a>
<ul class="sectlevel3">
<li><a href="#_api_and_implementation">7.1.1. API and Implementation</a>
<ul class="sectlevel4">
<li><a href="#__code_metamodelvalidator_code"><code>MetaModelValidator</code></a></li>
<li><a href="#__code_visitor_code"><code>Visitor</code></a></li>
<li><a href="#__code_summarizingvisitor_code"><code>SummarizingVisitor</code></a></li>
</ul>
</li>
<li><a href="#_configuration">7.1.2. Configuration</a></li>
</ul>
</li>
<li><a href="#_ugbtb_programming-model_finetuning">7.2. Finetuning</a>
<ul class="sectlevel3">
<li><a href="#_modifying_the_prog_model">7.2.1. Modifying the Prog. Model</a>
<ul class="sectlevel4">
<li><a href="#_including_or_excluding_facets">Including or excluding facets</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#_ugbtb_programming-model_layout-metadata-reader">7.3. Layout Metadata Reader</a>
<ul class="sectlevel3">
<li><a href="#_api_and_implementation_2">7.3.1. API and Implementation</a>
<ul class="sectlevel4">
<li><a href="#_extended_api">Extended API</a></li>
</ul>
</li>
<li><a href="#_configuration_2">7.3.2. Configuration</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#_ugbtb_deployment">8. Deployment</a>
<ul class="sectlevel2">
<li><a href="#_ugbtb_deployment_cmd-line">8.1. Command Line (<code>WebServer</code>)</a></li>
<li><a href="#_ugbtb_deployment_tomcat">8.2. Deploying to Tomcat</a>
<ul class="sectlevel3">
<li><a href="#_externalized_configuration">8.2.1. Externalized Configuration</a></li>
<li><a href="#_jvm_args">8.2.2. JVM Args</a></li>
<li><a href="#_using_a_jndi_datasource">8.2.3. Using a JNDI Datasource</a></li>
</ul>
</li>
<li><a href="#_ugbtb_deployment_externalized-configuration">8.3. Externalized Configuration</a>
<ul class="sectlevel3">
<li><a href="#_ugbtb_deployment_externalized-configuration_Isis">8.3.1. Apache Isis' Config</a></li>
<li><a href="#_ugbtb_deployment_externalized-configuration_Shiro">8.3.2. Shiro Config</a></li>
<li><a href="#_ugbtb_deployment_externalized-configuration_Log4j">8.3.3. Log4j Config</a></li>
<li><a href="#_spring_config">8.3.4. Spring Config</a>
<ul class="sectlevel4">
<li><a href="#_an_alternative_approach">An alternative approach</a></li>
</ul>
</li>
<li><a href="#_see_also">8.3.5. See also</a></li>
</ul>
</li>
<li><a href="#_ugbtb_deployment_docker">8.4. Docker</a>
<ul class="sectlevel3">
<li><a href="#_using_an_code_overrides_properties_code_file">8.4.1. Using an <code>overrides.properties</code> file</a></li>
<li><a href="#_using_system_properties">8.4.2. Using system properties</a></li>
<li><a href="#_using_the_code_isis_opts_code_environment_variable">8.4.3. Using the <code>ISIS_OPTS</code> environment variable</a></li>
</ul>
</li>
<li><a href="#_ugbtb_deployment_gae">8.5. Deploying to Google App Engine</a>
<ul class="sectlevel3">
<li><a href="#_primary_keys_and_owned_unowned_relationships">8.5.1. Primary Keys and Owned/Unowned Relationships</a></li>
<li><a href="#_custom_types">8.5.2. Custom Types</a></li>
</ul>
</li>
<li><a href="#_ugbtb_deployment_neo4j">8.6. Neo4J</a>
<ul class="sectlevel3">
<li><a href="#_connectionurl">8.6.1. ConnectionURL</a></li>
<li><a href="#_update_pom_xml">8.6.2. Update pom.xml</a></li>
<li><a href="#_try_it_out">8.6.3. Try it out!</a></li>
</ul>
</li>
<li><a href="#_ugbtb_deployment_jvm-flags">8.7. JVM Flags</a>
<ul class="sectlevel3">
<li><a href="#_configuring_in_tomcat">8.7.1. Configuring in Tomcat</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#_ugbtb_web-xml">9. <code>web.xml</code></a>
<ul class="sectlevel2">
<li><a href="#_ugbtb_web-xml_servlet-context-listeners">9.1. Servlet Context Listeners</a>
<ul class="sectlevel3">
<li><a href="#__code_environmentloaderlistener_code_shiro">9.1.1. <code>EnvironmentLoaderListener</code> (Shiro)</a></li>
<li><a href="#__code_isiswebappbootstrapper_code">9.1.2. <code>IsisWebAppBootstrapper</code></a></li>
<li><a href="#__code_resteasybootstrap_code_resteasy">9.1.3. <code>ResteasyBootstrap</code> (RestEasy)</a></li>
</ul>
</li>
<li><a href="#_ugbtb_web-xml_servlets">9.2. Servlets</a>
<ul class="sectlevel3">
<li><a href="#__code_httpservletdispatcher_code_resteasy">9.2.1. <code>HttpServletDispatcher</code> (RestEasy)</a></li>
<li><a href="#__code_resourceservlet_code">9.2.2. <code>ResourceServlet</code></a></li>
</ul>
</li>
<li><a href="#_ugbtb_web-xml_filters">9.3. Filters</a>
<ul class="sectlevel3">
<li><a href="#__code_shirofilter_code_shiro">9.3.1. <code>ShiroFilter</code> (Shiro)</a></li>
<li><a href="#__code_isislogonexceptionfilter_code">9.3.2. <code>IsisLogOnExceptionFilter</code></a></li>
<li><a href="#__code_resourcecachingfilter_code">9.3.3. <code>ResourceCachingFilter</code></a></li>
<li><a href="#__code_wicketfilter_code">9.3.4. <code>WicketFilter</code></a></li>
<li><a href="#__code_isissessionfilter_code">9.3.5. <code>IsisSessionFilter</code></a></li>
<li><a href="#__code_isistransactionfilterforrestfulobjects_code">9.3.6. <code>IsisTransactionFilterForRestfulObjects</code></a></li>
</ul>
</li>
<li><a href="#_ugbtb_web-xml_context-parameters">9.4. Configuration Files</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</div>
<script src="../js/foundation/5.5.1/vendor/jquery.js"></script>
<script src="../js/foundation/5.5.1/foundation.min.js"></script>
<link href="../css/jquery.tocify/1.9.0/jquery.tocify.css" rel="stylesheet">
<script src="../js/jqueryui/1.11.4/jquery-ui.min.js"></script>
<script src="../js/jquery.tocify/1.9.0/jquery.tocify.js"></script>
<script type="text/javascript">
$(function () {
$("#toc").tocify({
scrollTo: 50,
extendPage: true,
context: "#doc-content",
highlightOnScroll: true,
hashGenerator: "pretty",
hideEffect: "slideUp",
selectors: "h2,h3,h4,h5"
});
$(".fallback-toc").hide();
});
</script>
<script type="text/javascript">
/****
$(document).foundation();
$(document).ready(function(){
// Cache selectors
var lastId,
topMenu = $("div#toc ul"),
topMenuHeight = 100,
menuItems = topMenu.find("a"),
menuItemsHrefs = menuItems.map(function(){
var item = $($(this).attr("href"));
if (item.length) { return item; }
});
// Bind click handler to menu items to scroll animation
menuItems.click(function(e){
var href = $(this).attr("href"),
offsetTop = href === "#" ? 0 : $(href).offset().top-topMenuHeight+1;
$('html, body').stop().animate({
scrollTop: offsetTop
}, 300);
e.preventDefault();
});
// Bind to scroll of window
$( window ).scroll(function(){
// Get container scroll position
var fromTop = $(this).scrollTop()+topMenuHeight;
var cur = menuItemsHrefs.map(function(){
if ($(this).offset().top < fromTop)
return this;
});
// Get the id of the current element
cur = cur[cur.length-1];
var id = cur && cur.length ? cur[0].id : "";
if (lastId !== id && id) {
scrollTo(id);
}
window.history.pushState({}, "", window.location.origin + window.location.pathname + "#" + id);
});
scrollTo = function(id) {
lastId = id;
menuItems
.removeClass("active");
menuItems
.parents()
.removeClass("active-region");
menuItems
.parents("ul").hide();
menuItems
.filter("[href=#"+id+"]")
.addClass("active");
menuItems
.filter("[href=#"+id+"]")
.parents("ul").show();
menuItems
.filter("[href=#"+id+"]")
.parent().children("ul").show();
menuItems
.filter("[href=#"+id+"]")
.parents("li").addClass("active-region");
}
menuItems
.removeClass("active");
menuItems
.parents()
.removeClass("active-region");
var syncMenuItem;
if(window.location.hash!=="") {
var menuItemFor = $.grep(menuItems, function(e) {
return e.hash === window.location.hash;
});
console.log(menuItemFor);
if(menuItemFor.length === 1) {
syncMenuItem = menuItemFor[0];
}
}
if(!syncMenuItem){
syncMenuItem = menuItems[0];
}
$(syncMenuItem).click();
});
***/
</script>
<script type="text/javascript">
$(document).ready(function(){
if("Documentation" === "Beyond the Basics") {
console.log( "processing 'Documentation'" );
$("#doc-content-left").removeClass("large-9").removeClass("medium-9").addClass("large-12").addClass("medium-12");
$("#doc-content-right").removeClass("large-3").removeClass("medium-3").hide();
}
});
</script>
<script>
$( document ).ready(function() {
(function() {
$(function() {
return $("#doc-content h2, #doc-content h3, #doc-content h4, #doc-content h5, #doc-content h6").each(function(i, el) {
var $el, icon, id;
$el = $(el);
id = $el.attr('id');
icon = '<i class="fa fa-link"></i>';
if (id) {
return $el.prepend($("<a />").addClass("header-link").attr("href", "#" + id).html(icon));
}
});
});
}).call(this);
/*
http://osvaldas.info/auto-hide-sticky-header
MIT license
*/
;( function( $, window, document, undefined )
{
'use strict';
var elSelector = '.header',
elClassHidden = 'header--hidden',
throttleTimeout = 500,
$element = $( elSelector );
if( !$element.length ) return true;
var $window = $( window ),
wHeight = 0,
wScrollCurrent = 0,
wScrollBefore = 0,
wScrollDiff = 0,
$document = $( document ),
dHeight = 0,
throttle = function( delay, fn )
{
var last, deferTimer;
return function()
{
var context = this, args = arguments, now = +new Date;
if( last && now < last + delay )
{
clearTimeout( deferTimer );
deferTimer = setTimeout( function(){ last = now; fn.apply( context, args ); }, delay );
}
else
{
last = now;
fn.apply( context, args );
}
};
};
$window.on( 'scroll', throttle( throttleTimeout, function()
{
dHeight = $document.height();
wHeight = $window.height();
wScrollCurrent = $window.scrollTop();
wScrollDiff = wScrollBefore - wScrollCurrent;
if( wScrollCurrent <= 0 ) // scrolled to the very top; element sticks to the top
$element.removeClass( elClassHidden );
else if( wScrollDiff > 0 && $element.hasClass( elClassHidden ) ) // scrolled up; element slides in
$element.removeClass( elClassHidden );
else if( wScrollDiff < 0 ) // scrolled down
{
if( wScrollCurrent + wHeight >= dHeight && $element.hasClass( elClassHidden ) ) // scrolled to the very bottom; element slides in
$element.removeClass( elClassHidden );
else // scrolled down; element slides out
$element.addClass( elClassHidden );
}
wScrollBefore = wScrollCurrent;
}));
})( jQuery, window, document );
});
</script>
</body>
</html>