content/versions/1.11.1/guides/rgsvc.html (8,679 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>Domain Services</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{font-weight: normal} table.CodeRay{border-collapse:separate;border-spacing:0;margin-bottom:0;border:0;background:none} table.CodeRay td{vertical-align: top} 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:#00} .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">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&trade;</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="_rgsvc">1. Domain Services</h2> <div class="sectionbody"> <div class="paragraph"> <p>This guide documents Apache Isis' domain services, both those that act as an <a href="#_rgsvc_api">API</a> (implemented by the framework for your domain objects to call), and those domain services that act as an <a href="#_rgsvc_spi">SPI</a> (implemented by your domain application and which are called by the framework).</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="ugsec.html">Security</a></p> </li> <li> <p><a href="ugtst.html">Testing</a></p> </li> <li> <p><a href="ugbtb.html">Beyond the Basics</a></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="#">Domain Services</a> (this guide)</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> </ul> </div> <div class="paragraph"> <p>The "supporting procedures" guides are:</p> </div> <div class="ulist"> <ul> <li> <p><a href="cgcon.html">Contributors' 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="_rgsvc_api">2. Domain Services API</h2> <div class="sectionbody"> <div class="paragraph"> <p>Apache Isis includes an extensive number of domain services for your domain objects to use; simply define the service as an annotated field and Apache Isis will inject the service into your object. These services' API are all defined in Apache Isis' applib (<code>o.a.i.core:isis-core-applib</code> module); this minimizes the coupling between your code and Apache Isis. It also allows you to easily mock out these services in your unit tests.</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>In addition there are a number of domain services that constitute an SPI; if present they are used by Apache Isis itself rather than by your domain objects. A good example of this is the <a href="#_rgsvc_spi_AuditingService"><code>AuditingService3</code></a> service. The SPI services are covered in the <a href="#_rgsvc_spi">Domain Services (SPI) chapter</a>.</p> </div> </td> </tr> </table> </div> <div class="paragraph"> <p>The table below summarizes all the APIs defined by Apache Isis. It also lists their corresponding implementation, either a default implementation provided by Apache Isis itself, or provided by one of the in (non-ASF) <a href="http://www.isisaddons.org">Isis Addons</a> modules.</p> </div> <table class="tableblock frame-all grid-all spread"> <caption class="title">Table 1. Domain Services</caption> <colgroup> <col style="width: 25%;"> <col style="width: 25%;"> <col style="width: 25%;"> <col style="width: 25%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">API</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-left valign-top">Implementation</th> <th class="tableblock halign-left valign-top">Notes</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_AcceptHeaderService"><code>o.a.i.applib.</code><br> <code>services.acceptheader</code><br> <code>AcceptHeaderService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Request-scoped access to HTTP Accept headers.</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>AcceptHeaderServiceDefault</code><br> <code>o.a.i.core</code><br> <code>isis-viewer-restfulobjects-rendering</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Populated only when the domain objects are accessed using the <a href="ugvro.html">Restful Objects viewer</a>.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_ActionInvocationContext"><code>o.a.i.applib.</code><br> <code>services.actinv</code><br> <code>ActionInvocation-</code><br> <code>Context</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Request-scoped access to whether action is invoked on object and/or on collection of objects</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>ActionInvocationContext</code><br> <code>o.a.i.core</code><br> <code>isis-core-applib</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>API is also concrete class</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_BackgroundService"><code>o.a.i.applib.</code><br> <code>services.background</code><br> <code>BackgroundService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Programmatic persistence of commands to be persisted (so can be executed by a background mechanism, eg scheduler)</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>BackgroundServiceDefault</code><br> <code>o.a.i.core</code><br> <code>isis-core-runtime</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>depends on:<br> <a href="#_rgsvc_spi_BackgroundCommandService"><code>BackgroundCommand-Service</code></a></p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_BookmarkService"><code>o.a.i.applib.</code><br> <code>services.bookmark</code><br> <code>BookmarkService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Convert object reference to a serializable "bookmark", and vice versa</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>BookmarkServiceDefault</code><br> <code>o.a.i.core</code><br> <code>isis-core-metamodel</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>related services: <code>BookmarkHolder-</code><br> <code>ActionContributions</code>, <code>BookmarkHolder-</code><br> <code>Association-</code><br> <code>Contributions</code></p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_ClockService"><code>o.a.i.applib.</code><br> <code>services.clock</code><br> <code>ClockService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Access the current time (and for testing, allow the time to be changed)</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>ClockService</code><br> <code>o.a.i.core</code><br> <code>isis-core-applib</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>API is also a concrete class.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_CommandContext"><code>o.a.i.applib.</code><br> <code>services.command</code><br> <code>CommandContext</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Request-scoped access the current action that is being invoked, represented as a command object</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>CommandContext</code><br> <code>o.a.i.core</code><br> <code>isis-core-applib</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>API is also a concrete class.<br> depends on:<br> <a href="#_rgsvc_spi_CommandService"><code>CommandService</code></a> for persistent <code>Command</code>, else in-memory impl. used</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_DeepLinkService"><code>o.a.i.applib</code><br> <code>services.deeplink</code><br> <code>DeepLinkService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Obtain a URL to a domain object (eg for use within an email or report)</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>DeepLinkServiceWicket</code><br> <code>o.a.i.viewer</code><br> <code>isis-viewer-wicket-impl</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Implementation only usable within Wicket viewer</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_DomainObjectContainer"><code>o.a.i.applib</code><br> <code>DomainObjectContainer</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Generic repository to search, create and delete objects. Also miscellaneous other functions, eg obtain title of object.</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>DomainObjectContainer-</code><br> <code>Default</code><br> <code>o.a.i.core</code><br> <code>isis-core-metamodel</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_EmailService"><code>o.a.i.applib.</code><br> <code>services.email</code><br> <code>EmailService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Send a HTML email, optionally with attachments.</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>EmailServiceDefault</code><br> <code>o.a.i.core</code><br> <code>isis-core-runtime</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_EventBusService"><code>o.a.i.applib.</code><br> <code>services.eventbus</code><br> <code>EventBusService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Programmatically post events to the internal event bus. Also used by Apache Isis itself to broadcast domain events:</p> </div> <div class="ulist"> <ul> <li> <p><a href="rgant.html#_rgant-Action_domainEvent"><code>Action#domainEvent()</code></a></p> </li> <li> <p><a href="rgant.html#_rgant-Property_domainEvent"><code>Property#domainEvent()</code></a></p> </li> <li> <p><a href="rgant.html#_rgant-Collection_domainEvent"><code>Collection#domainEvent()</code></a></p> </li> </ul> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>EventBusServiceJdo</code><br> <code>o.a.i.core</code><br> <code>isis-core-objectstore-jdo-datanucleus</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_FixtureScriptsDefault"><code>o.a.i.applib.</code><br> <code>services.</code><br> <code>fixturespec</code><br> <code>FixtureScriptsDefault</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Fallback implementation of <a href="rgcms.html#_rgcms_classes_super_FixtureScripts"><code>FixtureScripts</code></a>, providing the ability to execute fixture scripts.</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>FixtureScriptsDefault</code><br> <code>o.a.i.core</code><br> <code>isis-core-applib</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Interacts with <a href="#_rgsvc_spi_FixtureScriptsSpecificationProvider"><code>FixtureScripts- SpecificationProvider</code></a>.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_GuiceBeanProvider"><code>o.a.i.applib.</code><br> <code>services.guice</code><br> <code>GuiceBeanProvider</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Access to internal framework services initialized using Guice DI.</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>GuiceBeanProviderWicket</code><br> <code>o.a.i.core</code><br> <code>isis-viewer-wicket-impl</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_HomePageProviderService"><code>o.a.i.applib.</code><br> <code>services.homepage</code><br> <code>HomePageProviderService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Returns the home page object, if any is defined.</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>HomePageProvider</code><br> <code>ServiceDefault</code><br> <code>o.a.i.core</code><br> <code>isis-core-runtime</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Used by the default implementation of <a href="#_rgsvc_spi_RoutingService"><code>RoutingService</code></a>.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_IsisJdoSupport"><code>o.a.i.applib.</code><br> <code>services.jdosupport</code><br> <code>IsisJdoSupport</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Lower level access to the JDO Persistence API.</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>IsisJdoSupportImpl</code><br> <code>o.a.i.core</code><br> <code>isis-core-objectstore-jdo-datanucleus</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_JaxbService"><code>o.a.i.applib.</code><br> <code>services.jaxb</code><br> <code>JaxbService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Marshal and unmarshal JAXB-annotated view models to/from XML.</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>JaxbServiceDefault</code><br> <code>o.a.i.core</code><br> <code>isis-core-schema</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_MementoService"><code>o.a.i.applib.</code><br> <code>services.memento</code><br> <code>MementoService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Capture a serializable memento of a set of primitives or <a href="#_rgsvc_api_BookmarkService">bookmarks</a>. Primarily used internally, eg in support of commands/auditing.</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>MementoServiceDefault</code><br> <code>o.a.i.core</code><br> <code>isis-core-runtime</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_MetaModelService"><code>o.a.i.applib.</code><br> <code>services.metamodel</code><br> <code>MetaModelService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Access to certain information from the Apache Isis metamodel.</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>MetaModelServiceDefault</code><br> <code>o.a.i.core</code><br> <code>isis-core-metamodel</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_QueryResultsCache"><code>o.a.i.applib.</code><br> <code>services.</code><br> <code>queryresultscache</code><br> <code>QueryResultsCache</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Request-scoped caching of the results of queries (or any data set generated by a given set of input arguments).</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>QueryResultsCache</code><br> <code>o.a.i.core</code><br> <code>isis-core-applib</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>API is also a concrete class</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_Scratchpad"><code>o.a.i.applib.</code><br> <code>services.scratchpad</code><br> <code>Scratchpad</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Request-scoped service for interchanging information between and aggregating over multiple method calls; in particular for use by "bulk" actions (invoking of an action for all elements of a collection)</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>Scratchpad</code><br> <code>o.a.i.core</code><br> <code>isis-core-applib</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>API is also a concrete class</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_SudoService"><code>o.a.i.applib.</code><br> <code>services.sudo</code><br> <code>SudoService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>For use in testing while running <a href="rgcms.html#_rgcms_classes_super_FixtureScripts">fixture scripts</a>, allows a block of code to run as a specified user account.</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>SudoServiceDefault</code><br> <code>o.a.i.core</code><br> <code>isis-core-runtime</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>API is also a concrete class</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_SwaggerService"><code>o.a.i.applib.</code><br> <code>services.sudo</code><br> <code>SwaggerService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Generates <a href="http://swagger.io/">Swagger</a> spec files to describe the public and/or private RESTful APIs exposed by the <a href="ugvro.html">RestfulObjects viewer</a>. These can then be used with the <a href="http://swagger.io/swagger-ui/">Swagger UI</a> page to explore the REST API, or used to generate client-side stubs using the <a href="http://swagger.io/swagger-codegen/">Swagger codegen</a> tool, eg for use in a custom REST client app.</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>SwaggerServiceDefault</code><br> <code>o.a.i.core</code><br> <code>isis-core-metamodel</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>A <code>SwaggerServiceMenu</code> domain service is also provided which enables the swagger spec to be downloaded. Apache Isis' <a href="cgcon.html#_cgcon_isis-maven-plugin">Maven plugin</a> also provides a <a href="cgcon.html#_cgcon_isis-maven-plugin_swagger">swagger goal</a> which allows the spec file(s) to be generated at build time (eg so that client-side stubs can then be generated in turn).</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_WrapperFactory"><code>o.a.i.applib.</code><br> <code>services.wrapper</code><br> <code>WrapperFactory</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Interact with another domain object "as if" through the UI (enforcing business rules, firing domain events)</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>WrapperFactoryDefault</code><br> <code>o.a.i.core</code><br> <code>isis-core-wrapper</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_api_XmlSnapshotService"><code>o.a.i.applib.</code><br> <code>services.xmlsnapshot</code><br> <code>XmlSnapshotService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Generate an XML representation of an object and optionally a graph of related objects.</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>XmlSnapshotServiceDefault</code><br> <code>o.a.i.core</code><br> <code>isis-core-runtime</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div></div></td> </tr> </tbody> </table> <div class="paragraph"> <p>Key:</p> </div> <div class="ulist"> <ul> <li> <p><code>o.a.i</code> is an abbreviation for <code>org.apache.isis</code></p> </li> <li> <p><code>o.ia.m</code> is an abbreviation for <code>org.isisaddons.module</code></p> </li> </ul> </div> <div class="paragraph"> <p>There is also a number of deprecated domain services.</p> </div> <table class="tableblock frame-all grid-all spread"> <caption class="title">Table 2. Deprecated Domain Services</caption> <colgroup> <col style="width: 25%;"> <col style="width: 25%;"> <col style="width: 25%;"> <col style="width: 25%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">API</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-left valign-top">Implementation</th> <th class="tableblock halign-left valign-top">Notes</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>o.a.i.applib.</code><br> <code>annotation</code> <code>Bulk.InteractionContext</code></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Request-scoped access to whether action is invoked on object and/or on collection of objects</p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p><code>Bulk.InteractionContext</code><br> <code>o.a.i.core</code><br> <code>isis-core-applib</code></p> </div></div></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Replaced by <a href="#_rgsvc_api_ActionInvocationContext"><code>ActionInvocationContext</code></a></p> </div></div></td> </tr> </tbody> </table> <div class="paragraph"> <p>Key:</p> </div> <div class="ulist"> <ul> <li> <p><code>o.a.i</code> is an abbreviation for <code>org.apache.isis</code></p> </li> <li> <p><code>o.ia.m</code> is an abbreviation for <code>org.isisaddons.module</code></p> </li> </ul> </div> <div class="sect2"> <h3 id="_rgsvc_api_AcceptHeaderService">2.1. <code>AcceptHeaderService</code></h3> <div class="paragraph"> <p>The <code>AcceptHeaderService</code> domain service is a <a href="rgant.html#_rgant-RequestScoped"><code>@RequestScoped</code></a> service that simply exposes the HTTP <code>Accept</code> header to the domain. Its intended use is to support multiple versions of a REST API, where the responsibility for content negotiation (determining which version of the REST API is to be used) is managed by logic in the domain objects themselves.</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>As an alternative to performing content negotiation within the domain classes, the <a href="#_rgsvc_spi_ContentNegotiationService"><code>ContentNegotiationService</code></a> and <a href="#_rgsvc_spi_ContentMappingService"><code>ContentMappingService</code></a> SPI domain services allow the framework to perform the content negotiation responsibility.</p> </div> </td> </tr> </table> </div> <div class="sect3"> <h4 id="_api_implementation">2.1.1. API &amp; Implementation</h4> <div class="paragraph"> <p>The API defined by the service is:</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">@RequestScoped</span> <i class="conum" data-value="1"></i><b>(1)</b> <span class="directive">public</span> <span class="type">interface</span> <span class="class">AcceptHeaderService</span> { <span class="annotation">@Programmatic</span> <span class="predefined-type">List</span>&lt;MediaType&gt; getAcceptableMediaTypes(); <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>is <a href="rgant.html#_rgant-RequestScoped"><code>@RequestScoped</code></a>, so this domain service instance is scoped to a particular request and is then destroyed</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>returns the list of media types found in the HTTP Accept header.</td> </tr> </table> </div> <div class="paragraph"> <p>The default implementation is provided by <code>o.a.i.v.ro.rendering.service.acceptheader.AcceptHeaderServiceForRest</code>.</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 the service will only return a list when the request is initiated through the <a href="ugvro.html">Restful Objects viewer</a>. Otherwise the service will return <code>null</code>.</p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_usage">2.1.2. Usage</h4> <div class="paragraph"> <p>The intended use of this service is where there are multiple concurrent versions of a REST API, for backward compatibility of existing clients. The <code>AcceptHeaderService</code> allows the responsibility for content negotiation (determining which version of the REST API is to be used) to be performed by logic in the domain objects themselves.</p> </div> <div class="paragraph"> <p>The diagram below illustrated this:</p> </div> <div class="imageblock"> <div class="content"> <a class="image" href="images/reference-services-api/acceptheaderservice.png"><img src="images/reference-services-api/acceptheaderservice.png" alt="acceptheaderservice" width="700px"></a> </div> </div> <div class="paragraph"> <p>The REST request is submitted to a domain service with a <a href="rgant.html#_rgant_DomainService_nature">nature</a> of <code>VIEW_REST_ONLY</code> (<code>MyRestApi</code> in the diagram). This uses the <code>AcceptHeaderService</code> to obtain the values of the HTTP <code>Accept</code> header. Based on this it delegates to the appropriate underlying domain service (with a nature of <code>DOMAIN</code> so that they are not exposed in the REST API at all).</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 service does not define any conventions as to the format of the media types. The option is to use the media type&#8217;s type/subtype, eg <code>application/vnd.myrestapi-v1+json</code>; an alternative is to use a media type parameter as a hint, eg <code>application/json;x-my-rest-api-version=1</code> (where <code>x-my-rest-api-version</code> is the media type parameter).</p> </div> <div class="paragraph"> <p>The Restful Objects specification does this something similar with its own <code>x-ro-domain-type</code> media type parameter; this is used by the <a href="#_rgsvc_spi_ContentMappingService"><code>ContentMappingService</code></a> to determine how to map domain objects to view models/DTOs.</p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_registering_the_service">2.1.3. Registering the Service</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' default implementation of <code>ActionInvocationContext</code> class is automatically registered (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_ActionInvocationContext">2.2. <code>ActionInvocationContext</code></h3> <div class="paragraph"> <p>The <code>ActionInvocationContext</code> domain service is a <a href="rgant.html#_rgant-RequestScoped"><code>@RequestScoped</code></a> service intended to support the implementation of "bulk" actions annotated with <a href="rgant.html#_rgant-Action_invokeOn"><code>@Action#invokeOn()</code></a>. This allows the user to select multiple objects in a table and then invoke the same action against all of them.</p> </div> <div class="paragraph"> <p>When an action is invoked in this way, this service allows each object instance to "know where it is" in the collection; it acts a little like an iterator. In particular, an object can determine if it is the last object to be called, and so can perform special processing, eg to return a summary calculated result.</p> </div> <div class="sect3"> <h4 id="_api">2.2.1. API</h4> <div class="paragraph"> <p>The API defined by the service is:</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">@RequestScoped</span> <i class="conum" data-value="1"></i><b>(1)</b> <span class="directive">public</span> <span class="directive">static</span> <span class="type">class</span> <span class="class">ActionInvocationContext</span> { <span class="directive">public</span> InvokedOn getInvokedOn() { ... } <i class="conum" data-value="2"></i><b>(2)</b> <span class="directive">public</span> <span class="predefined-type">List</span>&lt;<span class="predefined-type">Object</span>&gt; getDomainObjects() { ... } <i class="conum" data-value="3"></i><b>(3)</b> <span class="directive">public</span> <span class="type">int</span> getSize() { ... } <span class="directive">public</span> <span class="type">int</span> getIndex() { ... } <i class="conum" data-value="4"></i><b>(4)</b> <span class="directive">public</span> <span class="type">boolean</span> isFirst() { ... } <span class="directive">public</span> <span class="type">boolean</span> isLast() { ... } }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>is <a href="rgant.html#_rgant-RequestScoped"><code>@RequestScoped</code></a>, so this domain service instance is scoped to a particular request and is then destroyed</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>an enum set to either <code>OBJECT</code> (if action has been invoked on a single object) or <code>COLLECTION</code> (if has been invoked on a collection).</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>returns the list of domain objects which are being acted upon</td> </tr> <tr> <td><i class="conum" data-value="4"></i><b>4</b></td> <td>is the 0-based index to the object being acted upon.</td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_usage_2">2.2.2. Usage</h4> <div class="paragraph"> <p>For actions that are void or that return null, Apache Isis will return to the list once executed. But for bulk actions that are non-void, Apache Isis will render the returned object/value from the last object invoked (and simply discards the object/value of all actions except the last).</p> </div> <div class="paragraph"> <p>One idiom is for the domain objects to also use the <a href="#_rgsvc_api_Scratchpad"><code>Scratchpad</code></a> service to share information, for example to aggregate values. The <code>ActionInvocationContext#isLast()</code> method can then be used to determine if all the information has been gathered, and then do something with it (eg derive variance across a range of values, render a graph etc).</p> </div> <div class="paragraph"> <p>More prosaically, the <code>ActoinInvocationContext</code> can be used to ensure that the action behaves appropriately depending on how it has been invoked (on a single object and/or a collection) whether it is called in bulk mode or regular mode. Here&#8217;s a snippet of code from the bulk action in the Isis addon example <a href="https://github.com/isisaddons/isis-app-todoapp/">todoapp</a> (not ASF):</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">ToDoItem</span> ... { <span class="annotation">@Action</span>(invokeOn=InvokeOn.OBJECTS_AND_COLLECTIONS) <span class="directive">public</span> ToDoItem completed() { setComplete(<span class="predefined-constant">true</span>); ... return actionInteractionContext.getInvokedOn() == InvokedOn.OBJECT ? <span class="local-variable">this</span> <i class="conum" data-value="1"></i><b>(1)</b> : <span class="predefined-constant">null</span>; <i class="conum" data-value="2"></i><b>(2)</b> } <span class="annotation">@Inject</span> ActionInteractionContext actionInteractionContext; }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>if invoked as a regular action, return this object;</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>otherwise (if invoked on collection of objects), return null, so that the <a href="ugvw.html">Wicket viewer</a> will re-render the list of objects</td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_registering_the_service_2">2.2.3. Registering the Service</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' default implementation of <code>ActionInvocationContext</code> class is automatically registered (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> <div class="sect3"> <h4 id="_unit_testing_support">2.2.4. Unit testing support</h4> <div class="paragraph"> <p>The <code>ActionInvocationContext</code> class also has a couple of static factory methods intended to support unit testing:</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">@RequestScoped</span> <span class="directive">public</span> <span class="type">class</span> <span class="class">ActionInvocationContext</span> { <span class="directive">public</span> <span class="directive">static</span> ActionInvocationContext onObject(<span class="directive">final</span> <span class="predefined-type">Object</span> domainObject) { <span class="keyword">return</span> <span class="keyword">new</span> ActionInvocationContext(InvokedOn.OBJECT, <span class="predefined-type">Collections</span>.singletonList(domainObject)); } <span class="directive">public</span> <span class="directive">static</span> ActionInvocationContext onCollection(<span class="directive">final</span> <span class="predefined-type">Object</span>... domainObjects) { <span class="keyword">return</span> onCollection(<span class="predefined-type">Arrays</span>.asList(domainObjects)); } <span class="directive">public</span> <span class="directive">static</span> ActionInvocationContext onCollection(<span class="directive">final</span> <span class="predefined-type">List</span>&lt;<span class="predefined-type">Object</span>&gt; domainObjects) { <span class="keyword">return</span> <span class="keyword">new</span> ActionInvocationContext(InvokedOn.COLLECTION, domainObjects); } ... }</code></pre> </div> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_BackgroundService">2.3. <code>BackgroundService</code></h3> <div class="paragraph"> <p>The <code>BackgroundService</code>, and the companion <a href="#_rgsvc_spi_BackgroundCommandService"><code>BackgroundCommandService</code></a> SPI service (used by Apache Isis itself), enable action invocations to be persisted such that they may be invoked in the background.</p> </div> <div class="paragraph"> <p>The <code>BackgroundService</code> is responsible for capturing a memento representing the action invocation in a typesafe way, and persisting it rather than executing it directly.</p> </div> <div class="paragraph"> <p>The default <code>BackgroundServiceDefault</code> implementation (provided by isis-core) works by using a proxy wrapper around the target so that it can capture the action to invoke and its arguments using (a private copy of) <a href="#_rgsvc_api_MementoService"><code>MementoService</code></a>. The persistence delegates the persistence of the memento to an appropriate implementation of the companion <code>BackgroundCommandService</code>. One such implementation of <code>BackgroundCommandService</code> is provided by (non-ASF) <a href="http://github.com/isisaddons/isis-module-command">Isis addons' command</a> module.</p> </div> <div class="paragraph"> <p>The persisting of commands is only half the story; there needs to be a separate process to read the commands and execute them. The <code>BackgroundCommandExecution</code> abstract class (discussed <a href="#_rgsvc_api_BackgroundService_BackgroundCommandExecution">below</a>) provides infrastructure to do this; the concrete implementation of this class depends on the configured <code>BackgroundCommandService</code> (in order to query for the persisted (background) <code>Command</code>s.</p> </div> <div class="sect3"> <h4 id="_api_implementation_2">2.3.1. API &amp; Implementation</h4> <div class="paragraph"> <p>Returns a proxy around the object which is then used to obtain the signature of the action to be invoked in the background.</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">BackgroundService</span> { <span class="annotation">@Programmatic</span> &lt;T&gt; T execute(<span class="directive">final</span> T object); }</code></pre> </div> </div> <div class="paragraph"> <p>The default implementation is provided by core (<code>o.a.i.core.runtime.services.background.BackgroundServiceDefault</code>).</p> </div> </div> <div class="sect3"> <h4 id="_usage_3">2.3.2. Usage</h4> <div class="paragraph"> <p>Using the service is very straight-forward; wrap the target domain object using <code>BackgroundService#execute(&#8230;&#8203;)</code> and invoke the method on the object returned by that method.</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">void</span> submitCustomerInvoices() { <span class="keyword">for</span>(Customer customer: customerRepository.findCustomersToInvoice()) { backgroundService.execute(customer).submitInvoice(); } container.informUser(<span class="string"><span class="delimiter">&quot;</span><span class="content">Calculating...</span><span class="delimiter">&quot;</span></span>); }</code></pre> </div> </div> <div class="paragraph"> <p>This will create a bunch of background commands executing the <code>submitInvoice()</code> action for each of the customers returned from the customer repository.</p> </div> <div class="paragraph"> <p>The action method invoked must be part of the Apache Isis metamodel, which is to say it must be public, accept only scalar arguments, and must not be annotated with <a href="rgant.html#_rgant-Programmatic"><code>@Programmatic</code></a> or <code>@Ignore</code>. However, it may be annotated with <a href="rgant.html#_rgant-Action_hidden"><code>@Action#hidden()</code></a> or <a href="rgant.html#_rgant-ActionLayout_hidden"><code>@ActionLayout#hidden()</code></a> and it will still be invoked.</p> </div> <div class="paragraph"> <p>In fact, when invoked by the background service, no business rules (hidden, disabled, validation) are enforced; the action method must take responsibility for performing appropriate validation and error checking.</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>If you want to check business rules, you can use <a href="rgant.html#_rgant-WrapperFactory"><code>@WrapperFactory#wrapNoExecute(&#8230;&#8203;)</code></a>.</p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_end_user_experience">2.3.3. End-user experience</h4> <div class="paragraph"> <p>For the end-user, executing an action that delegates work off to the <code>BackgroundService</code> raises the problem of how does the user know the work is complete?</p> </div> <div class="paragraph"> <p>One option is for the background jobs to take responsibility to notify the user themselves. In the above example, this would be the <code>submitInvoice()</code> method called upon each customer. One could imagine more complex designs where only the final command executed notifies the user.</p> </div> <div class="paragraph"> <p>However, an alternative is to rely on the fact that the <code>BackgroundService</code> will automatically hint that the <code>Command</code> representing the original interaction (to <code>submitCustomerInvoices()</code> in the example above) should be persisted. This will be available if the related <a href="#_rgsvc_api_CommandContext"><code>CommandContext</code></a> and <a href="#_rgsvc_spi_CommandService"><code>CommandService</code></a> domain services are configured, and the <code>CommandService</code> supports persistent commands. Note that (non-ASF) <a href="http://github.com/isisaddons/isis-module-command">Isis addons' command</a> module does indeed provide such an implementation of <code>CommandService</code> (as well as of the required <code>BackgroundCommandService</code>).</p> </div> <div class="paragraph"> <p>Thus, the original action can run a query to obtain it corresponding <code>Command</code>, and return this to the user. The upshot is that the child <code>Command</code>s created by the <code>BackgroundService</code> will then be associated with <code>Command</code> for the original action.</p> </div> <div class="paragraph"> <p>We could if we wanted write the above example as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> Command submitCustomerInvoices() { <span class="keyword">for</span>(Customer customer: customerRepository.findCustomersToInvoice()) { backgroundService.execute(customer).submitInvoice(); } <span class="keyword">return</span> commandContext.getCommand(); } <span class="annotation">@Inject</span> CommandContext commandContext; <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 injected <a href="#_rgsvc_api_CommandContext"><code>CommandContext</code></a> domain service.</td> </tr> </table> </div> <div class="paragraph"> <p>The user would be returned a domain object representing their action invocation.</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_services">2.3.4. Registering the Services</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' core implementation of <code>BackgroundService</code> is automatically registered (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> <div class="sect3"> <h4 id="_related_services">2.3.5. Related Services</h4> <div class="paragraph"> <p>This service is closely related to the <a href="#_rgsvc_api_CommandContext"><code>CommandContext</code></a> and also that service&#8217;s supporting <a href="#_rgsvc_spi_CommandService"><code>CommandService</code></a> service.</p> </div> <div class="paragraph"> <p>The <code>CommandContext</code> service is responsible for providing a parent <code>Command</code> with which the background <code>Command</code>s can then be associated as children, while the <code>CommandService</code> is responsible for persisting those parent <code>Command`s. The latter is analogous to the way in which the `BackgroundCommandService</code> persists the child background `Command`s.</p> </div> <div class="paragraph"> <p>The implementations of <code>CommandService</code> and <code>BackgroundCommandService</code> go together; typically both parent <code>Command`s and child background `Command`s will be persisted in the same way. The (non-ASF) <a href="http://github.com/isisaddons/isis-module-command">Isis addons' command</a> module provides implementations of both (see <a href="#_rgsvc_spi_CommandService">`CommandService</code></a> and <a href="#_rgsvc_spi_BackgroundCommandService"><code>BackgroundCommandService</code></a>).</p> </div> </div> <div class="sect3"> <h4 id="_rgsvc_api_BackgroundService_BackgroundCommandExecution">2.3.6. <code>BackgroundCommandExecution</code> abstract class</h4> <div class="paragraph"> <p>The <code>BackgroundCommandExecution</code> (in isis-core) is an abstract template class provided by isis-core that defines an abstract hook method to obtain background `Command`s to be executed:</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 { ... protected <span class="directive">abstract</span> <span class="predefined-type">List</span>&lt;? <span class="directive">extends</span> Command&gt; findBackgroundCommandsToExecute(); ... }</code></pre> </div> </div> <div class="paragraph"> <p>The developer is required to implement this hook method in a subclass.</p> </div> </div> <div class="sect3"> <h4 id="_rgsvc_api_BackgroundService_Quartz">2.3.7. Quartz Scheduler Configuration</h4> <div class="paragraph"> <p>The last part of the puzzle is to actually run the (appropriate implementation of) `BackgroundCommandExecution). This could be run in a batch job overnight, or run continually by, say, the <a href="http://quartz-scheduler.org">Quartz</a> scheduler or by <a href="http://camel.apache.org" class="bare">http://camel.apache.org</a>]Apache Camel]. This section looks at configuring Quartz.</p> </div> <div class="paragraph"> <p>If using (non-ASF) <a href="http://github.com/isisaddons/isis-module-command">Isis addons' command</a> module, then note that this already provides a suitable concrete implementation, namely <code>org.isisaddons.module.command.dom.BackgroundCommandExecutionFromBackgroundCommandServiceJdo</code>. We therefore just need to schedule this to run as a Quartz job.</p> </div> <div class="paragraph"> <p>First, we need to define a Quartz job, for example:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">org.isisaddons.module.command.dom.BackgroundCommandExecutionFromBackgroundCommandServiceJdo</span>; <span class="directive">public</span> <span class="type">class</span> <span class="class">BackgroundCommandExecutionQuartzJob</span> <span class="directive">extends</span> AbstractIsisQuartzJob { <span class="directive">public</span> BackgroundCommandExecutionQuartzJob() { <span class="local-variable">super</span>(<span class="keyword">new</span> BackgroundCommandExecutionFromBackgroundCommandServiceJdo()); } }</code></pre> </div> </div> <div class="paragraph"> <p>where <code>AbstractIsisQuartzJob</code> is in turn the following boilerplate:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">package</span> <span class="namespace">domainapp.webapp.quartz</span>; <span class="keyword">import</span> <span class="include">org.quartz.Job</span>; <span class="keyword">import</span> <span class="include">org.quartz.JobExecutionContext</span>; <span class="keyword">import</span> <span class="include">org.quartz.JobExecutionException</span>; ... public <span class="type">class</span> <span class="class">AbstractIsisQuartzJob</span> <span class="directive">implements</span> Job { <span class="directive">public</span> <span class="directive">static</span> <span class="type">enum</span> ConcurrentInstancesPolicy { SINGLE_INSTANCE_ONLY, MULTIPLE_INSTANCES } <span class="directive">private</span> <span class="directive">final</span> AbstractIsisSessionTemplate isisRunnable; <span class="directive">private</span> <span class="directive">final</span> ConcurrentInstancesPolicy concurrentInstancesPolicy; <span class="directive">private</span> <span class="type">boolean</span> executing; <span class="directive">public</span> AbstractIsisQuartzJob(AbstractIsisSessionTemplate isisRunnable) { <span class="local-variable">this</span>(isisRunnable, ConcurrentInstancesPolicy.SINGLE_INSTANCE_ONLY); } <span class="directive">public</span> AbstractIsisQuartzJob( AbstractIsisSessionTemplate isisRunnable, ConcurrentInstancesPolicy concurrentInstancesPolicy) { <span class="local-variable">this</span>.isisRunnable = isisRunnable; <span class="local-variable">this</span>.concurrentInstancesPolicy = concurrentInstancesPolicy; } <span class="directive">public</span> <span class="type">void</span> execute(<span class="directive">final</span> JobExecutionContext context) <span class="directive">throws</span> JobExecutionException { <span class="directive">final</span> AuthenticationSession authSession = newAuthSession(context); <span class="keyword">try</span> { <span class="keyword">if</span>(concurrentInstancesPolicy == ConcurrentInstancesPolicy.SINGLE_INSTANCE_ONLY &amp;&amp; executing) { <span class="keyword">return</span>; } executing = <span class="predefined-constant">true</span>; isisRunnable.execute(authSession, context); } <span class="keyword">finally</span> { executing = <span class="predefined-constant">false</span>; } } AuthenticationSession newAuthSession(JobExecutionContext context) { <span class="predefined-type">String</span> user = getKey(context, SchedulerConstants.USER_KEY); <span class="predefined-type">String</span> rolesStr = getKey(context, SchedulerConstants.ROLES_KEY); <span class="predefined-type">String</span><span class="type">[]</span> roles = Iterables.toArray( Splitter.on(<span class="string"><span class="delimiter">&quot;</span><span class="content">,</span><span class="delimiter">&quot;</span></span>).split(rolesStr), <span class="predefined-type">String</span>.class); <span class="keyword">return</span> <span class="keyword">new</span> SimpleSession(user, roles); } <span class="predefined-type">String</span> getKey(JobExecutionContext context, <span class="predefined-type">String</span> key) { <span class="keyword">return</span> context.getMergedJobDataMap().getString(key); } }</code></pre> </div> </div> <div class="paragraph"> <p>This job can then be configured to run using Quartz' <code>quartz-config.xml</code> file:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="xml"><span class="preprocessor">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span> <span class="tag">&lt;job-scheduling-data</span> <span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">http://www.quartz-scheduler.org/xml/JobSchedulingData</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">xmlns:xsi</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">http://www.w3.org/2001/XMLSchema-instance</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">xsi:schemaLocation</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">http://www.quartz-scheduler.org/xml/JobSchedulingData</span> <span class="content">http://www.quartz-scheduler.org/xml/job_scheduling_data_1_8.xsd</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">version</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">1.8</span><span class="delimiter">&quot;</span></span><span class="tag">&gt;</span> <span class="tag">&lt;schedule&gt;</span> <span class="tag">&lt;job&gt;</span> <span class="tag">&lt;name&gt;</span>BackgroundCommandExecutionJob<span class="tag">&lt;/name&gt;</span> <span class="tag">&lt;group&gt;</span>Isis<span class="tag">&lt;/group&gt;</span> <span class="tag">&lt;description&gt;</span> Poll and execute any background actions persisted by the BackgroundActionServiceJdo domain service <span class="tag">&lt;/description&gt;</span> <span class="tag">&lt;job-class&gt;</span>domainapp.webapp.quartz.BackgroundCommandExecutionQuartzJob<span class="tag">&lt;/job-class&gt;</span> <span class="tag">&lt;job-data-map&gt;</span> <span class="tag">&lt;entry&gt;</span> <span class="tag">&lt;key&gt;</span>webapp.scheduler.user<span class="tag">&lt;/key&gt;</span> <span class="tag">&lt;value&gt;</span>scheduler_user<span class="tag">&lt;/value&gt;</span> <span class="tag">&lt;/entry&gt;</span> <span class="tag">&lt;entry&gt;</span> <span class="tag">&lt;key&gt;</span>webapp.scheduler.roles<span class="tag">&lt;/key&gt;</span> <span class="tag">&lt;value&gt;</span>admin_role<span class="tag">&lt;/value&gt;</span> <span class="tag">&lt;/entry&gt;</span> <span class="tag">&lt;/job-data-map&gt;</span> <span class="tag">&lt;/job&gt;</span> <span class="tag">&lt;trigger&gt;</span> <span class="tag">&lt;cron&gt;</span> <span class="tag">&lt;name&gt;</span>BackgroundCommandExecutionJobEveryTenSeconds<span class="tag">&lt;/name&gt;</span> <span class="tag">&lt;job-name&gt;</span>BackgroundCommandExecutionJob<span class="tag">&lt;/job-name&gt;</span> <span class="tag">&lt;job-group&gt;</span>Isis<span class="tag">&lt;/job-group&gt;</span> <span class="tag">&lt;cron-expression&gt;</span>0/10 * * * * ?<span class="tag">&lt;/cron-expression&gt;</span> <span class="tag">&lt;/cron&gt;</span> <span class="tag">&lt;/trigger&gt;</span> <span class="tag">&lt;/schedule&gt;</span> <span class="tag">&lt;/job-scheduling-data&gt;</span></code></pre> </div> </div> <div class="paragraph"> <p>The remaining two pieces of configuration are the <code>quartz.properties</code> file:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="ini">org.quartz.scheduler.instanceName = SchedulerQuartzConfigXml org.quartz.threadPool.threadCount = 1 org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore org.quartz.plugin.jobInitializer.class =org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin org.quartz.plugin.jobInitializer.fileNames = webapp/scheduler/quartz-config.xml org.quartz.plugin.jobInitializer.failOnFileNotFound = true</code></pre> </div> </div> <div class="paragraph"> <p>and the entry in <code>web.xml</code> for the Quartz servlet:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;servlet&gt;</span> <span class="tag">&lt;servlet-name&gt;</span>QuartzInitializer<span class="tag">&lt;/servlet-name&gt;</span> <span class="tag">&lt;servlet-class&gt;</span>org.quartz.ee.servlet.QuartzInitializerServlet<span class="tag">&lt;/servlet-class&gt;</span> <span class="tag">&lt;init-param&gt;</span> <span class="tag">&lt;param-name&gt;</span>config-file<span class="tag">&lt;/param-name&gt;</span> <span class="tag">&lt;param-value&gt;</span>webapp/scheduler/quartz.properties<span class="tag">&lt;/param-value&gt;</span> <span class="tag">&lt;/init-param&gt;</span> <span class="tag">&lt;init-param&gt;</span> <span class="tag">&lt;param-name&gt;</span>shutdown-on-unload<span class="tag">&lt;/param-name&gt;</span> <span class="tag">&lt;param-value&gt;</span>true<span class="tag">&lt;/param-value&gt;</span> <span class="tag">&lt;/init-param&gt;</span> <span class="tag">&lt;init-param&gt;</span> <span class="tag">&lt;param-name&gt;</span>start-scheduler-on-load<span class="tag">&lt;/param-name&gt;</span> <span class="tag">&lt;param-value&gt;</span>true<span class="tag">&lt;/param-value&gt;</span> <span class="tag">&lt;/init-param&gt;</span> <span class="tag">&lt;load-on-startup&gt;</span>1<span class="tag">&lt;/load-on-startup&gt;</span> <span class="tag">&lt;/servlet&gt;</span></code></pre> </div> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_BookmarkService">2.4. <code>BookmarkService</code></h3> <div class="paragraph"> <p>The <code>BookmarkService</code> provides the ability to obtain a serializable <code>o.a.i.applib.bookmarks.Bookmark</code> for any (persisted) domain object, and to lookup domain objects given a <code>Bookmark</code>. This can then in turn be converted to and from a string.</p> </div> <div class="paragraph"> <p>For example, a <code>Customer</code> object with:</p> </div> <div class="ulist"> <ul> <li> <p>an object type of "custmgmt.Customer" (as per <a href="rgant.html#_rgant-DomainObject_objectType"><code>DomainObject#objectType()</code></a> or equivalent) , and</p> </li> <li> <p>an id=123</p> </li> </ul> </div> <div class="paragraph"> <p>could correspond to a <code>Bookmark</code> with a string representation of <code>custmgmt.Customer|123</code>.</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>A <code>Bookmark</code> is little more than an API equivalent of Apache Isis' internal <code>Oid</code> (object identifier). Nevertheless, the ability to uniquely address <em>any</em> domain object within an Apache Isis system&#8201;&#8212;&#8201;to in effect provide a URN&#8201;&#8212;&#8201;is immensely useful.</p> </div> <div class="paragraph"> <p>For example, a <code>Bookmark</code> could be converted into a barcode, and then this used for automated scanning of correspondence from a customer.</p> </div> </td> </tr> </table> </div> <div class="paragraph"> <p><code>Bookmark</code>s are used by several other domain services as a means of storing areference to an arbitrary object (a polymorphic relationship). For example, the (non-ASF) <a href="http://github.com/isisaddons/isis-module-auditing">Isis addons' auditing</a> module&#8217;s implementation of <a href="#_rgsvc_spi_AuditingService"><code>AuditingService</code></a> uses bookmarks to capture the object that is being audited.</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 downside of using <code>Bookmark</code>s is that there is no way for the JDO/DataNucleus objectstore to enforce any kind of referental integrity. However, the (non-ASF) <a href="http://github.com/isisaddons/isis-module-poly">Isis addons' poly</a> module describes and supports a design pattern to address this requirement.</p> </div> </td> </tr> </table> </div> <div class="sect3"> <h4 id="_api_implementation_3">2.4.1. API &amp; Implementation</h4> <div class="paragraph"> <p>The API defined by <code>BookmarkService</code> 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">interface</span> <span class="class">BookmarkService</span> { <span class="annotation">@Programmatic</span> <span class="predefined-type">Object</span> lookup(BookmarkHolder bookmarkHolder); <span class="annotation">@Programmatic</span> <span class="predefined-type">Object</span> lookup(Bookmark bookmark); <span class="annotation">@Programmatic</span> &lt;T&gt; T lookup(Bookmark bookmark, <span class="predefined-type">Class</span>&lt;T&gt; cls); <i class="conum" data-value="1"></i><b>(1)</b> <span class="annotation">@Programmatic</span> Bookmark bookmarkFor(<span class="predefined-type">Object</span> domainObject); <span class="annotation">@Programmatic</span> Bookmark bookmarkFor(<span class="predefined-type">Class</span>&lt;?&gt; cls, <span class="predefined-type">String</span> identifier); }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>same as <code>lookup(Bookmark bookmark)</code>, but downcasts to the specified type.</td> </tr> </table> </div> <div class="paragraph"> <p>The core framework provides a default implementation of this API, namely <code>o.a.i.core.metamodel.services.bookmarks.BookmarkServiceDefault</code></p> </div> </div> <div class="sect3"> <h4 id="__code_bookmarkholder_code">2.4.2. <code>BookmarkHolder</code></h4> <div class="paragraph"> <p>The <code>BookmarkHolder</code> interface is intended to be implemented by domain objects that use a <code>Bookmark</code> to reference a (single) domain object; an example might be a class such as the audit entry, mentioned above. The interface is simply:</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">BookmarkHolder</span> { <span class="annotation">@Programmatic</span> Bookmark bookmark(); }</code></pre> </div> </div> <div class="paragraph"> <p>There are two services that will contribute to this interface:</p> </div> <div class="ulist"> <ul> <li> <p><code>BookmarkHolderActionContributions</code> will provide a <code>lookup(&#8230;&#8203;)</code> action</p> </li> <li> <p><code>BookmarkHolderAssociationContributions</code> provides an <code>object</code> property.</p> </li> </ul> </div> <div class="paragraph"> <p>Either of these can be suppressed, if required, using a vetoing subscriber. For example, to suppress the <code>object</code> property (so that only the <code>lookup(&#8230;&#8203;)</code> action is ever shown for implementations of <code>BookmarkHolder</code>, define:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@DomainObject</span> <span class="directive">public</span> <span class="type">class</span> <span class="class">AlwaysHideBookmarkHolderAssociationsObjectProperty</span> { <span class="annotation">@Subscribe</span> <span class="directive">public</span> <span class="type">void</span> on(BookmarkHolderAssociationContributions.ObjectDomainEvent ev) { ev.hide(); } }</code></pre> </div> </div> <div class="paragraph"> <p>A more sophisticated implementation could look inside the passed <code>ev</code> argument and selectively hide or not based on the contributee.</p> </div> </div> <div class="sect3"> <h4 id="_usage_by_other_services">2.4.3. Usage by other services</h4> <div class="paragraph"> <p>Bookmarks are used by the (non-ASF) <a href="http://github.com/isisaddons/isis-module-command">Isis addons' command</a> module&#8217;s implementation of <a href="#_rgsvc_api_BackgroundCommandService"><code>BackgroundCommandService</code></a>, which uses a bookmark to capture the target object on which an action will be invoked subsequently.</p> </div> <div class="paragraph"> <p>Bookmarks are also used by the (non-ASF) <a href="http://github.com/isisaddons/isis-module-publishing">Isis addons' publishing</a> module&#8217;s implementation of <a href="#_rgsvc_spi_PublishingService"><code>PublishingService</code></a>, and by the (non-ASF) <a href="http://github.com/isisaddons/isis-module-auditing">Isis addons' auditing</a> module&#8217;s implementation of <a href="#_rgsvc_spi_AuditingService"><code>AuditingService</code></a>.</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_service_3">2.4.4. Registering the Service</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' core implementation of <code>BookmarkService</code> is automatically registered (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_ClockService">2.5. <code>ClockService</code></h3> <div class="paragraph"> <p>Most applications deal with dates and times in one way or another. For example, if an <code>Order</code> is placed, then the <code>Customer</code> may have 30 days to pay the <code>Invoice</code>, otherwise a penalty may be levied.</p> </div> <div class="paragraph"> <p>However, such date/time related functionality can quickly complicate automated testing: "today+30" will be a different value every time the test is run.</p> </div> <div class="paragraph"> <p>Even disregarding testing, there may be a requirement to ensure that date/times are obtained from an NNTP server (rather than the system PC). While instantiating a <code>java.util.Date</code> to current the current time is painless enough, we would not want complex technical logic for querying an NNTP server spread around domain logic code.</p> </div> <div class="paragraph"> <p>Therefore it&#8217;s common to provide a domain service whose responsibility is to provide the current time. This service can be injected into any domain object (and can be mocked out for unit testing). Apache Isis provides such a facade through the <code>ClockService</code>.</p> </div> <div class="sect3"> <h4 id="_api_implementation_4">2.5.1. API &amp; Implementation</h4> <div class="paragraph"> <p>The API defined by <code>ClockService</code> is:</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">ClockService</span> { <span class="annotation">@Programmatic</span> <span class="directive">public</span> LocalDate now() { ... } <span class="annotation">@Programmatic</span> <span class="directive">public</span> LocalDateTime nowAsLocalDateTime() { ... } <span class="annotation">@Programmatic</span> <span class="directive">public</span> DateTime nowAsDateTime() { ... } <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="predefined-type">Timestamp</span> nowAsJavaSqlTimestamp() { ... } <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="type">long</span> nowAsMillis() { ... } }</code></pre> </div> </div> <div class="paragraph"> <p>This class (<code>o.a.i.applib.services.clock.ClockService</code>) is also the default implementation. The time provided by this default implementation is based on the system clock.</p> </div> </div> <div class="sect3"> <h4 id="_testing_support">2.5.2. Testing Support</h4> <div class="paragraph"> <p>The default <code>ClockService</code> implementation in fact simply delegates to another class defined in the API, namely the <code>o.a.i.applib.clock.Clock</code>, an abstract singleton class. It is not recommended that your code use the <code>Clock</code> directly, but you should understand how this all works:</p> </div> <div class="ulist"> <ul> <li> <p>there are two subclasses implementations <code>Clock</code>, namely <code>SystemClock</code> and <code>FixtureClock</code>.</p> </li> <li> <p>the first implementation that is instantiated registers itself as the singleton.</p> </li> <li> <p>if running in <a href="rgcfg.html#_rgcfg_deployment-types">production</a> (server) mode, then (unless another implementation has beaten it to the punch) the framework will instantiate the <code>`SystemClock</code>. Once instantiated this cannot be replaced.</p> </li> <li> <p>if running in <a href="rgcfg.html#<em>rgcfg_deployment-types">prototype</a> mode, then the framework will instead instantiate <code>FixtureClock</code>. This _can</em> be replaced if required.</p> </li> </ul> </div> <div class="paragraph"> <p>The <code>FixtureClock</code> will behave as the system clock, unless its is explicitly set using <code>FixtureClock#setDate(&#8230;&#8203;)</code> or <code>FixtureClock#setTime(&#8230;&#8203;)</code> and so forth.</p> </div> <div class="sect4"> <h5 id="_alternative_implementations">Alternative Implementations</h5> <div class="paragraph"> <p>Suppose you want (as discussed in the introduction to this service) to use a clock that delegates to NNTP. For most domain services this would amount to implementing the appropriate service and registering in <code>isis.properties</code> so that it is used in preference to any implementations provided by default by the framework.</p> </div> <div class="paragraph"> <p>In the case of the <code>ClockService</code>, though, this approach (unfortunately) will not work, because parts of Apache Isis (still) delegate to the <code>Clock</code> singleton rather than using the <code>ClockService</code> domain service.</p> </div> <div class="paragraph"> <p>The workaround, therefore, is to implement your functionality as a subclass of <code>Clock</code>. You can write a domain service that will ensure that your implementation is used ahead of any implementations provided by the framework.</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">@DomainService</span>(nature=NatureOfService.DOMAIN) <span class="directive">public</span> <span class="type">class</span> <span class="class">NntpClockServiceInitializer</span> { <span class="annotation">@Programmatic</span> <span class="annotation">@PostConstruct</span> <span class="directive">public</span> <span class="type">void</span> postConstruct(<span class="predefined-type">Map</span>&lt;<span class="predefined-type">String</span>,<span class="predefined-type">String</span>&gt; properties) { <span class="keyword">new</span> NntpClock(properties); <i class="conum" data-value="1"></i><b>(1)</b> } <span class="directive">private</span> <span class="directive">static</span> <span class="type">class</span> <span class="class">NntpClock</span> <span class="directive">extends</span> Clock { NntpClock(<span class="predefined-type">Map</span>&lt;<span class="predefined-type">String</span>,<span class="predefined-type">String</span>&gt; properties) { ... } <i class="conum" data-value="2"></i><b>(2)</b> <span class="directive">protected</span> <span class="type">long</span> time() { ... } <i class="conum" data-value="3"></i><b>(3)</b> ... NNTP stuff here ... } } }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>enough to simply instantiate the <code>Clock</code>; it will register itself as singleton</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>connect to NNTP service using configuration properties from <code>isis.properties</code></td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>call to NNTP service here</td> </tr> </table> </div> </div> </div> <div class="sect3"> <h4 id="_registering_the_service_4">2.5.3. Registering the Service</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' core implementation of <code>ClockService</code> is automatically registered (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>If you want to use a different implementation of <code>Clock</code>, eg delegating to NNTP, then do <em>not</em> register directly, but instead subclass from <code>o.a.i.applib.clock.Clock</code> singleton (as described in the section above).</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_CommandContext">2.6. <code>CommandContext</code></h3> <div class="paragraph"> <p>The <code>CommandContext</code> service is a <a href="rgant.html#_rgant-RequestScoped">request-scoped</a> service that reifies the invocation of an action on a domain object into an object itself. This reified information is encapsulated within the <code>Command</code> object.</p> </div> <div class="paragraph"> <p>By default, the <code>Command</code> is held in-memory only; once the action invocation has completed, the <code>Command</code> object is gone. The optional supporting <a href="#_rgsvc_spi_CommandService"><code>CommandService</code></a> enables the implementation of <code>Command</code> to be pluggable. With an appropriate implementation (eg as provided by the (non-ASF) <a href="http://github.com/isisaddons/isis-module-command">Isis addons' command</a> module&#8217;s <a href="#_rgsvc_spi_CommandService"><code>CommandService</code></a>) the <code>Command</code> may then be persisted.</p> </div> <div class="paragraph"> <p>Persistent <code>Command</code>s support several use cases:</p> </div> <div class="ulist"> <ul> <li> <p>they enable profiling of the running application (which actions are invoked then most often, what is their response time)</p> </li> <li> <p>they act as a parent to any background commands that might be invoked through the <a href="#_rgsvc_api_BackgroundService"><code>BackgroundService</code></a></p> </li> <li> <p>if <a href="#_rgsvc_spi_AuditingService"><code>AuditingService</code></a> is configured, they provide better audit information, since the <code>Command</code> (the 'cause' of an action) can be correlated to the audit records (the "effect" of the action) through the unique <code>transactionId</code> GUID</p> </li> <li> <p>if <a href="#_rgsvc_spi_PublishingService"><code>PublishingService</code></a> is configured, they provide better traceability as the <code>Command</code> is also correlated with any published events, again through the unique <code>transactionId</code> GUID</p> </li> </ul> </div> <div class="paragraph"> <p>Assuming that the <code>CommandService</code> supports persistent <code>Command`s, the associated <a href="rgant.html#_rgant-Action_command"></code>@Action#command()<code></a> annotation also allows action invocations to be performed in the background. In this case the act of invoking the action on an object instead returns the `Command</code> to the user.</p> </div> <div class="sect3"> <h4 id="_rgsvc_api_CommandContext_screencast">2.6.1. Screencast</h4> <div class="paragraph"> <p>The screencast provides a run-through of the command (profiling) service, auditing service, publishing service. It also shows how commands can be run in the background either explicitly by scheduling through the background service or implicitly by way of a framework annotation.</p> </div> <div class="videoblock"> <div class="content"> <iframe width="640px" height="360px" src="//www.youtube.com/embed/tqXUZkPB3EI?rel=0" frameborder="0" allowfullscreen></iframe> </div> </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 screencast shows an earlier version of the <a href="ugvw.html">Wicket viewer</a> UI (specifically, pre 1.8.0).</p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_api_implementation_5">2.6.2. API &amp; Implementation</h4> <div class="paragraph"> <p>The <code>CommandContext</code> request-scoped service defines the following very simple API:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@RequestScoped</span> <span class="directive">public</span> <span class="type">class</span> <span class="class">CommandContext</span> { <span class="annotation">@Programmatic</span> <span class="directive">public</span> Command getCommand() { ... } }</code></pre> </div> </div> <div class="paragraph"> <p>This class (<code>o.a.i.applib.services.CommandContext</code>) is also the default implementation. Under normal circumstances there shouldn&#8217;t be any need to replace this implementation with another.</p> </div> <div class="paragraph"> <p>The <code>Command</code> type referenced above is in fact an interface, defined as:</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">Command</span> <span class="directive">extends</span> HasTransactionId { <span class="directive">public</span> <span class="directive">abstract</span> <span class="predefined-type">String</span> getUser(); <i class="conum" data-value="1"></i><b>(1)</b> <span class="directive">public</span> <span class="directive">abstract</span> <span class="predefined-type">Timestamp</span> getTimestamp(); <i class="conum" data-value="2"></i><b>(2)</b> <span class="directive">public</span> <span class="directive">abstract</span> Bookmark getTarget(); <i class="conum" data-value="3"></i><b>(3)</b> <span class="directive">public</span> <span class="directive">abstract</span> <span class="predefined-type">String</span> getMemberIdentifier(); <i class="conum" data-value="4"></i><b>(4)</b> <span class="directive">public</span> <span class="directive">abstract</span> <span class="predefined-type">String</span> getTargetClass(); <i class="conum" data-value="5"></i><b>(5)</b> <span class="directive">public</span> <span class="directive">abstract</span> <span class="predefined-type">String</span> getTargetAction(); <i class="conum" data-value="6"></i><b>(6)</b> <span class="directive">public</span> <span class="predefined-type">String</span> getArguments(); <i class="conum" data-value="7"></i><b>(7)</b> <span class="directive">public</span> <span class="predefined-type">String</span> getMemento(); <i class="conum" data-value="8"></i><b>(8)</b> <span class="directive">public</span> ExecuteIn getExecuteIn(); <i class="conum" data-value="9"></i><b>(9)</b> <span class="directive">public</span> <span class="predefined-type">Executor</span> getExecutor(); <i class="conum" data-value="10"></i><b>(10)</b> <span class="directive">public</span> Persistence getPersistence(); <i class="conum" data-value="11"></i><b>(11)</b> <span class="directive">public</span> <span class="type">boolean</span> isPersistHint(); <i class="conum" data-value="12"></i><b>(12)</b> <span class="directive">public</span> <span class="directive">abstract</span> <span class="predefined-type">Timestamp</span> getStartedAt(); <i class="conum" data-value="13"></i><b>(13)</b> <span class="directive">public</span> <span class="directive">abstract</span> <span class="predefined-type">Timestamp</span> getCompletedAt(); <i class="conum" data-value="14"></i><b>(14)</b> <span class="directive">public</span> Command getParent(); <i class="conum" data-value="15"></i><b>(15)</b> <span class="directive">public</span> Bookmark getResult(); <i class="conum" data-value="16"></i><b>(16)</b> <span class="directive">public</span> <span class="predefined-type">String</span> getException(); <i class="conum" data-value="17"></i><b>(17)</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>getUser()</code> - is the user that initiated the action.</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td><code>getTimestamp()</code> - the date/time at which this action was created.</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td><code>getTarget()</code> - bookmark of the target object (entity or service) on which this action was performed</td> </tr> <tr> <td><i class="conum" data-value="4"></i><b>4</b></td> <td><code>getMemberIdentifier()</code> - holds a string representation of the invoked action</td> </tr> <tr> <td><i class="conum" data-value="5"></i><b>5</b></td> <td><code>getTargetClass()</code> - a human-friendly description of the class of the target object</td> </tr> <tr> <td><i class="conum" data-value="6"></i><b>6</b></td> <td><code>getTargetAction()</code> - a human-friendly name of the action invoked on the target object</td> </tr> <tr> <td><i class="conum" data-value="7"></i><b>7</b></td> <td><code>getArguments()</code> - a human-friendly description of the arguments with which the action was invoked</td> </tr> <tr> <td><i class="conum" data-value="8"></i><b>8</b></td> <td><code>getMemento()</code> - a formal (XML or similar) specification of the action to invoke/being invoked</td> </tr> <tr> <td><i class="conum" data-value="9"></i><b>9</b></td> <td><code>getExecuteIn()</code> - whether this command is executed in the foreground or background</td> </tr> <tr> <td><i class="conum" data-value="10"></i><b>10</b></td> <td><code>getExecutor()</code> - the (current) executor of this command, either user, or background service, or other (eg redirect after post).</td> </tr> <tr> <td><i class="conum" data-value="11"></i><b>11</b></td> <td><code>getPersistence()</code>- the policy controlling whether this command should ultimately be persisted (either "persisted", "if hinted", or "not persisted")</td> </tr> <tr> <td><i class="conum" data-value="12"></i><b>12</b></td> <td><code>isPersistHint()</code> - whether that the command should be persisted, if persistence policy is "if hinted".</td> </tr> <tr> <td><i class="conum" data-value="13"></i><b>13</b></td> <td><code>getStartedAt()</code> - the date/time at which this action started (same as <code>timestamp</code> property for foreground commands)</td> </tr> <tr> <td><i class="conum" data-value="14"></i><b>14</b></td> <td><code>getCompletedAt()</code> - the date/time at which this action completed.</td> </tr> <tr> <td><i class="conum" data-value="15"></i><b>15</b></td> <td><code>getParent()</code> - for actions created through the <code>BackgroundService</code>, captures the parent action</td> </tr> <tr> <td><i class="conum" data-value="16"></i><b>16</b></td> <td><code>getResult()</code> - bookmark to object returned by action, if any</td> </tr> <tr> <td><i class="conum" data-value="17"></i><b>17</b></td> <td><code>getException()</code> - exception stack trace if action threw exception</td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_usage_4">2.6.3. Usage</h4> <div class="paragraph"> <p>The typical way to indicate that an action should be treated as a command is to annotate it with the <a href="rgant.html#_rgant-Action_command"><code>@Action#command()</code></a> annotation.</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">ToDoItem</span> ... { <span class="annotation">@Action</span>(command=CommandReification.ENABLED) <span class="directive">public</span> ToDoItem completed() { ... } }</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>As an alternative to annotating every action with <code>@Action#command()</code>, alternatively this can be configured as the default using <code>isis.services.command.actions</code> configuration property.</p> </div> <div class="paragraph"> <p>See <a href="rgant.html#_rgant-Action_command"><code>@Action#command()</code></a> and <a href="rgcfg.html#_rgcfg_configuring-core">runtime configuration</a> for further details.</p> </div> </td> </tr> </table> </div> <div class="paragraph"> <p>The <a href="rgant.html#_rgant-Action_command"><code>@Action#command()</code></a> annotation can also be used to specify whether the command should be performed in the background, 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">ToDoItem</span> ... { <span class="annotation">@Command</span>(executeIn=ExecuteIn.BACKGROUND) <span class="directive">public</span> ToDoItem scheduleImplicitly() { completeSlowly(<span class="integer">3000</span>); <span class="keyword">return</span> <span class="local-variable">this</span>; } }</code></pre> </div> </div> <div class="paragraph"> <p>When a background command is invoked, the user is returned the command object itself (to provide a handle to the command being invoked).</p> </div> <div class="paragraph"> <p>This requires that an implementation of <a href="#_rgsvc_spi_CommandService"><code>CommandService</code></a> that persists the commands (such as the (non-ASF) <a href="http://github.com/isisaddons/isis-module-command">Isis addons' command</a> module&#8217;s <code>CommandService</code>) is configured. It also requires that a scheduler is configured to execute the background commands, see <a href="#_rgsvc_spi_BackgroundCommandService"><code>BackgroundCommandService</code></a>).</p> </div> </div> <div class="sect3"> <h4 id="_interacting_with_the_services">2.6.4. Interacting with the services</h4> <div class="paragraph"> <p>Typically the domain objects have little need to interact with the <code>CommandContext</code> and <code>Command</code> directly; what is more useful is that these are persisted in support of the various use cases identified above.</p> </div> <div class="paragraph"> <p>One case however where a domain object might want to obtain the <code>Command</code> is to determine whether it has been invoked in the foreground, or in the background. It can do this using the <code>getExecutedIn()</code> method:</p> </div> <div class="paragraph"> <p>Although not often needed, this then allows the domain object to access the <code>Command</code> object through the <code>CommandContext</code> service. To expand th 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">ToDoItem</span> ... { <span class="annotation">@Action</span>( command=CommandReification.ENABLED, commandExecuteIn=CommandExecuteIn.BACKGROUND ) <span class="directive">public</span> ToDoItem completed() { ... Command currentCommand = commandContext.getCommand(); ... } <span class="annotation">@Inject</span> CommandContext commandContext; }</code></pre> </div> </div> <div class="paragraph"> <p>If run in the background, it might then notify the user (eg by email) if all work is done.</p> </div> <div class="paragraph"> <p>This leads us onto a related point, distinguishing the current effective user vs the originating "real" user. When running in the foreground, the current user can be obtained from the <code>DomainObjectContainer</code>, using:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="predefined-type">String</span> user = container.getUser().getName();</code></pre> </div> </div> <div class="paragraph"> <p>If running in the background, however, then the current user will be the credentials of the background process, for example as run by a Quartz scheduler job.</p> </div> <div class="paragraph"> <p>The domain object can still obtain the original ("effective") user that caused the job to be created, using:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="predefined-type">String</span> user = commandContext.getCommand().getUser();</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_registering_the_services_2">2.6.5. Registering the Services</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' core implementation of <code>CommandContext</code> service is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> <div class="sect3"> <h4 id="_related_services_2">2.6.6. Related Services</h4> <div class="paragraph"> <p>As discussed above, the supporting <a href="#_rgsvc_spi_CommandService"><code>CommandService</code></a> enables <code>Command</code> objects to be persisted. Other related services are the <a href="#_rgsvc_api_BackgroundService"><code>BackgroundService</code></a> and <a href="#_rgsvc_spi_BackgroundCommandService"><code>BackgroundCommandService</code></a>). For <code>BackgroundService</code> captures commands for execution in the background, while the [BackgroundCommandService] persists such commands for execution.</p> </div> <div class="paragraph"> <p>The implementations of <code>CommandService</code> and <code>BackgroundCommandService</code> are intended to go together, so that persistent parent `Command`s can be associated with their child background `Command`s.</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_DeepLinkService">2.7. <code>DeepLinkService</code></h3> <div class="paragraph"> <p>The <code>DeepLinkService</code> provides the ability to obtain a <code>java.net.URI</code> that links to a representation of any (persisted) domain entity or view model.</p> </div> <div class="paragraph"> <p>A typical use case is to generate a clickable link for rendering in an email, PDF, tweet or other communication.</p> </div> <div class="sect3"> <h4 id="_api_implementation_6">2.7.1. API &amp; Implementation</h4> <div class="paragraph"> <p>The API defined by <code>DeepLinkService</code> 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">interface</span> <span class="class">DeepLinkService</span> { <span class="predefined-type">URI</span> deepLinkFor(<span class="predefined-type">Object</span> domainObject); <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>Creates a URI that can be used to obtain a representation of the provided domain object in one of the Apache Isis viewers.</td> </tr> </table> </div> <div class="paragraph"> <p>The <a href="ugvw.html">Wicket viewer</a> this provides an implementation for accessing the representation through this viewer. (For the <a href="ugvro.html">RestfulObjects viewer</a>, a URL can be constructed according to the <a href="http://www.restfulobjects.org">Restful Objects spec</a> in conjunction with a <code>Bookmark</code> obtained via the <a href="#_rgsvc_api_BookmarkService"><code>BookmarkService</code></a>).</p> </div> </div> <div class="sect3"> <h4 id="_usage_within_the_framework">2.7.2. Usage within the framework</h4> <div class="paragraph"> <p>The <a href="#_rgsvc_spi_EmailNotificationService"><code>EmailNotificationService</code></a> uses this service in order to generate emails as part of <a href="ugvw.html#_ugvw_features_user-registration">user registration</a>.</p> </div> </div> <div class="sect3"> <h4 id="_implementations">2.7.3. Implementations</h4> <div class="paragraph"> <p>The Wicket viewer core framework provides a default implementation of this API:</p> </div> <div class="ulist"> <ul> <li> <p><code>org.apache.isis.viewer.wicket.viewer.services.DeepLinkServiceWicket</code></p> </li> </ul> </div> </div> <div class="sect3"> <h4 id="_registering_the_services_3">2.7.4. Registering the Services</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#<em>rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>), _and</em> that the <a href="ugvw.html">Wicket viewer</a> is being used, then an implementation of <code>DeepLinkService</code> is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_DomainObjectContainer">2.8. <code>DomainObjectContainer</code></h3> <div class="paragraph"> <p>The <code>DomainObjectContainer</code> service provides a set of general purpose functionality for domain objects to call. Principal amongst these are a generic APIs for querying objects and creating and persisting objects. In addition, the service provides access to security context (the "current user"), allows information and warning messages to be raised, and various other miscellaneous functions.</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>Compared to other services provided by Apache Isis, the <code>DomainObjectContainer</code> provides by far the most number of methods, addressing several quite distinct responsibilities. This is a historical accident: in early versions of Apache Isis the <code>DomainObjectContainer</code> was the one and only domain service provided by the framework.</p> </div> <div class="paragraph"> <p>In the future we may deprecate this service and break out its responsibilities into a number of small services. However, given how important this service was in days past, we are unlikely to delete it as a service even once it has been deprecated.</p> </div> </td> </tr> </table> </div> <div class="paragraph"> <p>The sections below discuss the functions provided by the service, broken out into categories.</p> </div> <div class="sect3"> <h4 id="_rgsvc_api_DomainObjectContainer_object-creation-api">2.8.1. Object Creation API</h4> <div class="paragraph"> <p>The object creation APIs are used to instantiate new domain objects or view models.</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="annotation">@Programmatic</span> &lt;T&gt; T newTransientInstance(<span class="directive">final</span> <span class="predefined-type">Class</span>&lt;T&gt; ofType); <i class="conum" data-value="1"></i><b>(1)</b> <span class="annotation">@Programmatic</span> &lt;T&gt; T newViewModelInstance(<span class="directive">final</span> <span class="predefined-type">Class</span>&lt;T&gt; ofType, <span class="directive">final</span> <span class="predefined-type">String</span> memento); <i class="conum" data-value="2"></i><b>(2)</b> <span class="annotation">@Programmatic</span> &lt;T&gt; T mixin(); <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>create a new non-persisted domain entity. Any services will be automatically injected into the service</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>create a new view model, with the specified memento (as per <a href="rgcms.html#_rgcms_classes_super_AbstractViewModel">ViewModel#viewModelMemento()</a>. In general it is easier to just annotate with <a href="rgant.html#_rgant-ViewModel"><code>@ViewModel</code></a> and let Apache Isis manage the memento automatically.</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>programmatically instantiate a mixin, as annotated with <a href="rgant.html#_rgant-Mixin"><code>@Mixin</code></a> or <a href="rgant.html#_rgant-DomainObject_nature"><code>@DomainObject#nature()</code></a>.</td> </tr> </table> </div> <div class="paragraph"> <p>For example:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Customer cust = container.newTransientInstance(Customer.class); cust.setFirstName(<span class="string"><span class="delimiter">&quot;</span><span class="content">Freddie</span><span class="delimiter">&quot;</span></span>); cust.setLastName(<span class="string"><span class="delimiter">&quot;</span><span class="content">Mercury</span><span class="delimiter">&quot;</span></span>); container.persist(cust);</code></pre> </div> </div> <div class="paragraph"> <p>As an alternative to using <code>newTransientInstance(&#8230;&#8203;)</code> or <code>mixin(&#8230;&#8203;)</code>, you could also simply <code>new()</code> up the object. Doing this will not inject any domain services, but they can be injected manually using <a href="rg .html#_rgsvc_api_DomainObjectContainer_services-api">#injectServicesInto(&#8230;&#8203;)`</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>Calling <code>new(&#8230;&#8203;)</code> also this circumvents Apache Isis' <a href="rgcms.html#_rgcms_methods_reserved_created"><code>created()</code></a> callback, and in addition any default values for properties (either explicitly set by <a href="rgcms.html#_rgcms_methods_prefixes_default"><code>default&#8230;&#8203;()</code></a> or defaulted implicitly according to Apache Isis' own conventions) will not be called either. If you don&#8217;t intend to use these features, though, the net effect is code that has less coupling to Isis and is arguably easier to understand (has "less magic" happening).</p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_rgsvc_api_DomainObjectContainer_generic-repository-api">2.8.2. Generic Repository API</h4> <div class="paragraph"> <p>The repository API acts as an abstraction over the JDO/DataNucleus objectstore. You can use it during prototyping to write naive queries (find all rows, then filter using the Guava <code>Predicate</code> API, or you can use it to call JDO <a href="http://www.datanucleus.org/products/accessplatform_4_0/jdo/query.html#named">named queries</a> using JDOQL.</p> </div> <div class="paragraph"> <p>As an alternative, you could also use <a href="http://www.datanucleus.org/products/accessplatform_4_0/jdo/jdoql_typesafe.html">JDO typesafe queries</a> through the <a href="#_rgsvc_api_IsisJdoSupport"><code>IsisJdoSupport</code></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">DomainObjectContainer</span> { <span class="annotation">@Programmatic</span> <span class="directive">public</span> &lt;T&gt; <span class="predefined-type">List</span>&lt;T&gt; allInstances(<span class="predefined-type">Class</span>&lt;T&gt; ofType, <span class="type">long</span>... range); <i class="conum" data-value="1"></i><b>(1)</b> <span class="annotation">@Programmatic</span> &lt;T&gt; <span class="predefined-type">List</span>&lt;T&gt; allMatches(<span class="predefined-type">Query</span>&lt;T&gt; query); <i class="conum" data-value="2"></i><b>(2)</b> <span class="annotation">@Programmatic</span> &lt;T&gt; <span class="predefined-type">List</span>&lt;T&gt; allMatches(<span class="predefined-type">Class</span>&lt;T&gt; ofType, <span class="predefined-type">Predicate</span>&lt;? <span class="local-variable">super</span> T&gt; predicate, <span class="type">long</span>... range); <i class="conum" data-value="3"></i><b>(3)</b> <span class="annotation">@Programmatic</span> &lt;T&gt; <span class="predefined-type">List</span>&lt;T&gt; allMatches(<span class="predefined-type">Class</span>&lt;T&gt; ofType, <span class="predefined-type">String</span> title, <span class="type">long</span>... range); <i class="conum" data-value="4"></i><b>(4)</b> <span class="annotation">@Programmatic</span> &lt;T&gt; <span class="predefined-type">List</span>&lt;T&gt; allMatches(<span class="predefined-type">Class</span>&lt;T&gt; ofType, T pattern, <span class="type">long</span>... range); <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>all persisted instances of specified type. Mostly for prototyping, though can be useful to obtain all instances of domain entities if the number is known to be small. The optional varargs parameters are for paging control; more on this below.</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>all persistence instances matching the specified <code>Query</code>. Query itself is an Isis abstraction on top of JDO/DataNucleus' Query API. <strong>This is the primary API used for querying</strong></td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>all persistenced instances of specified type matching <code>Predicate</code>. Only really intended for prototyping because in effect constitutes a client-side WHERE clause</td> </tr> <tr> <td><i class="conum" data-value="4"></i><b>4</b></td> <td>all persisted instances with the specified string as their title. Only very occasionally used</td> </tr> <tr> <td><i class="conum" data-value="5"></i><b>5</b></td> <td>all persisted instances matching object (query-by-example). Only very occasionally used</td> </tr> </table> </div> <div class="paragraph"> <p>There are various implementations of the <code>Query</code> API, but these either duplicate functionality of the other overloads of <code>allMatches(&#8230;&#8203;)</code> or they are not supported by the JDO/DataNucleus object store. The only significant implementation of <code>Query</code> to be aware of is <code>QueryDefault</code>, which identifies a named query and a set of parameter/argument tuples.</p> </div> <div class="paragraph"> <p>For example, in the (non-ASF) <a href="http://github.com/isisaddons/isis-app-todoapp">Isis addons' todoapp</a> the <code>ToDoItem</code> is annotated:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@javax</span>.jdo.annotations.Queries( { <span class="annotation">@javax</span>.jdo.annotations.Query( name = <span class="string"><span class="delimiter">&quot;</span><span class="content">findByAtPathAndComplete</span><span class="delimiter">&quot;</span></span>, language = <span class="string"><span class="delimiter">&quot;</span><span class="content">JDOQL</span><span class="delimiter">&quot;</span></span>, <i class="conum" data-value="1"></i><b>(1)</b> value = <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT </span><span class="delimiter">&quot;</span></span> + <span class="string"><span class="delimiter">&quot;</span><span class="content">FROM todoapp.dom.module.todoitem.ToDoItem </span><span class="delimiter">&quot;</span></span> + <span class="string"><span class="delimiter">&quot;</span><span class="content">WHERE atPath.indexOf(:atPath) == 0 </span><span class="delimiter">&quot;</span></span> <i class="conum" data-value="2"></i><b>(2)</b> + <span class="string"><span class="delimiter">&quot;</span><span class="content"> &amp;&amp; complete == :complete</span><span class="delimiter">&quot;</span></span>), <i class="conum" data-value="3"></i><b>(3)</b> ... }) <span class="directive">public</span> <span class="type">class</span> <span class="class">ToDoItem</span> ... { ... }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>name of the query</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>defines the <code>atPath</code> parameter</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>defines the <code>complete</code> parameter</td> </tr> </table> </div> <div class="paragraph"> <p>This JDO query definitions are used in the <code>ToDoItemRepositoryImplUsingJdoql</code> service:</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">ToDoItemRepositoryImplUsingJdoql</span> <span class="directive">implements</span> ToDoItemRepositoryImpl { <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="predefined-type">List</span>&lt;ToDoItem&gt; findByAtPathAndCategory(<span class="directive">final</span> <span class="predefined-type">String</span> atPath, <span class="directive">final</span> Category category) { <span class="keyword">return</span> container.allMatches( <span class="keyword">new</span> QueryDefault&lt;&gt;(ToDoItem.class, <span class="string"><span class="delimiter">&quot;</span><span class="content">findByAtPathAndCategory</span><span class="delimiter">&quot;</span></span>, <i class="conum" data-value="1"></i><b>(1)</b> <span class="string"><span class="delimiter">&quot;</span><span class="content">atPath</span><span class="delimiter">&quot;</span></span>, atPath, <i class="conum" data-value="2"></i><b>(2)</b> <span class="string"><span class="delimiter">&quot;</span><span class="content">category</span><span class="delimiter">&quot;</span></span>, category)); <i class="conum" data-value="3"></i><b>(3)</b> } ... <span class="annotation">@javax</span>.inject.Inject DomainObjectContainer container; }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>corresponds to the "findByAtPathAndCategory" JDO named query</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>provide argument for the <code>atPath</code> parameter. The pattern is parameter, argument, parameter, argument, &#8230;&#8203; and so on.</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>provide argument for the <code>category</code> parameter. The pattern is parameter, argument, parameter, argument, &#8230;&#8203; and so on.</td> </tr> </table> </div> <div class="paragraph"> <p>Other JDOQL named queries (not shown) follow the exact same pattern.</p> </div> <div class="paragraph"> <p>With respect to the other query APIs, the varargs parameters are optional, but allow for (client-side and managed) paging. The first parameter is the <code>start</code> (0-based, the second is the <code>count</code>.</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>It is also possible to query using DataNucleus' type-safe query API. For more details, see <a href="#_rgsvc_api_IsisJdoSupport"><code>IsisJdoSupport</code></a>.</p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_rgsvc_api_DomainObjectContainer_object-persistence-api">2.8.3. Object Persistence API</h4> <div class="paragraph"> <p>The persistence API is used to persist newly created objects (as per <a href="#_rgsvc_api_DomainObjectContainer_object-creation-api"><code>#newTransientInstance(&#8230;&#8203;)</code></a>, above and to delete (remove) objects that are persistent.</p> </div> <div class="paragraph"> <p>Note that there is no API for updating existing objects; the framework (or rather, JDO/DataNucleus) performs object dirty tracking and so any objects that are modified in the course of a request will be automatically updated).</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="annotation">@Programmatic</span> <span class="type">boolean</span> isPersistent(<span class="predefined-type">Object</span> domainObject); <i class="conum" data-value="1"></i><b>(1)</b> <span class="annotation">@Programmatic</span> <span class="type">boolean</span> isViewModel(<span class="predefined-type">Object</span> domainObject); <i class="conum" data-value="2"></i><b>(2)</b> <span class="annotation">@Programmatic</span> <span class="type">void</span> persist(<span class="predefined-type">Object</span> domainObject); <i class="conum" data-value="3"></i><b>(3)</b> <span class="annotation">@Programmatic</span> <span class="type">void</span> persistIfNotAlready(<span class="predefined-type">Object</span> domainObject); <i class="conum" data-value="4"></i><b>(4)</b> <span class="annotation">@Programmatic</span> <span class="type">void</span> remove(<span class="predefined-type">Object</span> persistentDomainObject); <i class="conum" data-value="5"></i><b>(5)</b> <span class="annotation">@Programmatic</span> <span class="type">void</span> removeIfNotAlready(<span class="predefined-type">Object</span> domainObject); <i class="conum" data-value="6"></i><b>(6)</b> <span class="annotation">@Programmatic</span> <span class="type">boolean</span> flush(); <i class="conum" data-value="7"></i><b>(7)</b> ... }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>test whether a particular domain object is persistent or not</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>test whether a particular domain object is a view model or not. Note that this includes any domain objects annotated with <a href="rgant.html#_rgant-DomainObject_nature"><code>@DomainObject#nature=Nature.EXTERNAL_ENTITY)</code></a> or <a href="rgant.html#_rgant-DomainObject_nature"><code>@DomainObject#nature=Nature.INMEMORY_ENTITY</code></a></td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>persist a transient object. Note though that this will throw an exception if the object is already persistent; this can happen if JDO/DataNucleus&#8217;s <a href="http://www.datanucleus.org/products/accessplatform_4_0/jdo/orm/cascading.html">persistence-by-reachability</a> is in effect. For this reason it is generally better to use:</td> </tr> <tr> <td><i class="conum" data-value="4"></i><b>4</b></td> <td>persist an object but only if know to not have been persistent. But if the object is persistent, is a no-op</td> </tr> <tr> <td><i class="conum" data-value="5"></i><b>5</b></td> <td>remove (ie DELETE) a persistent object. For similar reasons to the persistence, it is generally better to use:</td> </tr> <tr> <td><i class="conum" data-value="6"></i><b>6</b></td> <td>remove (ie DELETE) an object only if known to be persistent. But if the object has already been deleted, then is a no-op.</td> </tr> <tr> <td><i class="conum" data-value="7"></i><b>7</b></td> <td>flushes all pending changes to the objectstore. Explained further below.</td> </tr> </table> </div> <div class="paragraph"> <p>For example:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Customer cust = container.newTransientInstance(Customer.class); cust.setFirstName(<span class="string"><span class="delimiter">&quot;</span><span class="content">Freddie</span><span class="delimiter">&quot;</span></span>); cust.setLastName(<span class="string"><span class="delimiter">&quot;</span><span class="content">Mercury</span><span class="delimiter">&quot;</span></span>); container.persistIfNotAlready(cust);</code></pre> </div> </div> <div class="paragraph"> <p>You should be aware that by default Apache Isis queues up calls to <code>#persist()</code> and <code>#remove()</code>. These are then executed either when the request completes (and the transaction commits), or if the queue is flushed. This can be done either implicitly by the framework, or as the result of a direct call to <code>#flush()</code>.</p> </div> <div class="paragraph"> <p>By default the framework itself will cause <code>#flush()</code> to be called whenever a query is executed by way of <code>#allMatches(Query)</code>, as documented <a href="#_rgsvc_api_DomainObjectContainer_generic-repository-api">above</a>. However, this behaviour can be disabled using the <a href="rgcfg.html#_rgcfg_configuring-core">configuration property</a> <code>isis.services.container.disableAutoFlush</code>.</p> </div> </div> <div class="sect3"> <h4 id="_rgsvc_api_DomainObjectContainer_messages-api">2.8.4. Messages API</h4> <div class="paragraph"> <p>The <code>DomainObjectContainer</code> allows domain objects to raise information, warning or error messages. These messages can either be simple strings, or can be translated.</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="annotation">@Programmatic</span> <span class="type">void</span> informUser(<span class="predefined-type">String</span> message); <i class="conum" data-value="1"></i><b>(1)</b> <span class="annotation">@Programmatic</span> <span class="predefined-type">String</span> informUser(TranslatableString message, <span class="predefined-type">Class</span>&lt;?&gt; contextClass, <span class="predefined-type">String</span> contextMethod); <i class="conum" data-value="2"></i><b>(2)</b> <span class="annotation">@Programmatic</span> <span class="type">void</span> warnUser(<span class="predefined-type">String</span> message); <i class="conum" data-value="3"></i><b>(3)</b> <span class="annotation">@Programmatic</span> <span class="predefined-type">String</span> warnUser(TranslatableString message, <span class="predefined-type">Class</span>&lt;?&gt; contextClass, <span class="predefined-type">String</span> contextMethod); <i class="conum" data-value="4"></i><b>(4)</b> <span class="annotation">@Programmatic</span> <span class="type">void</span> raiseError(<span class="predefined-type">String</span> message); <i class="conum" data-value="5"></i><b>(5)</b> <span class="annotation">@Programmatic</span> <span class="predefined-type">String</span> raiseError(TranslatableString message, <span class="predefined-type">Class</span>&lt;?&gt; contextClass, <span class="predefined-type">String</span> contextMethod); <i class="conum" data-value="6"></i><b>(6)</b> ... }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>display as a transient message to the user (not requiring acknowledgement). In the <a href="ugvw.html">Wicket viewer</a> this is implemented as a toast that automatically disappears after a period of time.</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>ditto, but with translatable string, for <a href="ugbtb.html#_ugbtb_i18n">i18n support</a>.</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>warn the user about a situation with the specified message. In the <a href="ugvw.html">Wicket viewer</a> this is implemented as a toast that must be closed by the end-user.</td> </tr> <tr> <td><i class="conum" data-value="4"></i><b>4</b></td> <td>ditto, but with translatable string, for i18n support.</td> </tr> <tr> <td><i class="conum" data-value="5"></i><b>5</b></td> <td>show the user an unexpected application error. In the <a href="ugvw.html">Wicket viewer</a> this is implemented as a toast (with a different colour) that must be closed by the end-user.</td> </tr> <tr> <td><i class="conum" data-value="6"></i><b>6</b></td> <td>ditto, but with translatable string, for i18n support.</td> </tr> </table> </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> Order addItem(Product product, <span class="annotation">@ParameterLayout</span>(named=<span class="string"><span class="delimiter">&quot;</span><span class="content">Quantity) int quantity) { if(productRepository.stockLevel(product) == 0) { container.warnUser( product.getDescription() + </span><span class="delimiter">&quot;</span></span> out of stock; order fulfillment may be delayed<span class="string"><span class="delimiter">&quot;</span><span class="content">); } ... }</span></span></code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_rgsvc_api_DomainObjectContainer_security-api">2.8.5. Security API</h4> <div class="paragraph"> <p>The security API allows the domain object to obtain the identity of the user interacting with said object.</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="annotation">@Programmatic</span> UserMemento getUser(); ... }</code></pre> </div> </div> <div class="paragraph"> <p>where in turn (the essence of) <code>UserMemento</code> 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">final</span> <span class="type">class</span> <span class="class">UserMemento</span> { <span class="directive">public</span> <span class="predefined-type">String</span> getName() { ... } <span class="directive">public</span> <span class="type">boolean</span> isCurrentUser(<span class="directive">final</span> <span class="predefined-type">String</span> userName) { ... } <span class="directive">public</span> <span class="predefined-type">List</span>&lt;RoleMemento&gt; getRoles() { ... } <span class="directive">public</span> <span class="type">boolean</span> hasRole(<span class="directive">final</span> RoleMemento role) { ... } <span class="directive">public</span> <span class="type">boolean</span> hasRole(<span class="directive">final</span> <span class="predefined-type">String</span> roleName) { ... } ... }</code></pre> </div> </div> <div class="paragraph"> <p>and <code>RoleMemento</code> is simpler still:</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">RoleMemento</span> { <span class="directive">public</span> <span class="predefined-type">String</span> getName() { ... } <span class="directive">public</span> <span class="predefined-type">String</span> getDescription() { ... } ... }</code></pre> </div> </div> <div class="paragraph"> <p>The roles associated with the <code>UserMemento</code> will be based on the configured <a href="ugsec.html">security</a> (typically Shiro).</p> </div> <div class="paragraph"> <p>In addition, when using the <a href="ugvw.html">Wicket viewer</a> there will be an additional "org.apache.isis.viewer.wicket.roles.USER" role; this is used internally to restrict access to web pages without authenticating.</p> </div> </div> <div class="sect3"> <h4 id="_rgsvc_api_DomainObjectContainer_presentation-api">2.8.6. Presentation API</h4> <div class="paragraph"> <p>A responsibility of every domain object is to return a title. This can be done declaratively using the <a href="rgant.html#_rgant-Title"><code>@Title</code></a> annotation on property/ies, or it can be done imperatively by writing a <a href="rgcms.html#_rgcms_methods_reserved_title"><code>title()</code></a> method.</p> </div> <div class="paragraph"> <p>It&#8217;s quite common for titles to be built up of the titles of other objects. If using building up the title using <code>@Title</code> then Apache Isis will automatically use the title of the objects referenced by the annotated properties. We also need programmatic access to these titles if going the imperative route.</p> </div> <div class="paragraph"> <p>Similarly, it often makes sense if <a href="#_rgsvc_api_DomainObjectContainer_messages-api">raising messages</a> to use the title of an object in a message rather (than a some other property of the object), because this is how end-users will be used to identifying the object.</p> </div> <div class="paragraph"> <p>The API defined by <code>DomainObjectContainer</code> is simply:</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="annotation">@Programmatic</span> <span class="predefined-type">String</span> titleOf(<span class="predefined-type">Object</span> domainObject); <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>return the title of the object, as rendered in the UI by the Apache Isis viewers.</td> </tr> </table> </div> <div class="paragraph"> <p>By way of example, here&#8217;s some code from the (non-ASF) <a href="http://github.com/isisaddons/isis-app-todoapp">Isis addons' todoapp</a> showing the use of the API in an message:</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">List</span>&lt;ToDoItem&gt; delete() { <span class="directive">final</span> <span class="predefined-type">String</span> title = container.titleOf(<span class="local-variable">this</span>); <i class="conum" data-value="1"></i><b>(1)</b> ... container.removeIfNotAlready(<span class="local-variable">this</span>); container.informUser( TranslatableString.tr( <span class="string"><span class="delimiter">&quot;</span><span class="content">Deleted {title}</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">title</span><span class="delimiter">&quot;</span></span>, title), <i class="conum" data-value="2"></i><b>(2)</b> <span class="local-variable">this</span>.getClass(), <span class="string"><span class="delimiter">&quot;</span><span class="content">delete</span><span class="delimiter">&quot;</span></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 title is obtained first, because we&#8217;re not allowed to reference object after it&#8217;s been deleted</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>use the title in an i18n <code>TranslatableString</code></td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_rgsvc_api_DomainObjectContainer_validation-api">2.8.7. Validation API</h4> <div class="paragraph"> <p>The validation API allows a domain object to programmatically validate itself or any other domain object. Specifically, this means the validating the current state of all properties, as well as any object-level validation defined by <a href="rgcms.html#_rgcms_methods_reserved_validate"><code>validate()</code></a>.</p> </div> <div class="paragraph"> <p>The API provided 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">interface</span> <span class="class">DomainObjectContainer</span> { <span class="annotation">@Programmatic</span> <span class="type">boolean</span> isValid(<span class="predefined-type">Object</span> domainObject); <span class="annotation">@Programmatic</span> <span class="predefined-type">String</span> validate(<span class="predefined-type">Object</span> domainObject); ... }</code></pre> </div> </div> <div class="paragraph"> <p>If an object is changed by editing its properties, then (using the <a href="ugvw.html">Wicket viewer</a> at least) Apache Isis will check that the state of the object is valid. There is currently however no equivalent guarantee for actions.</p> </div> <div class="paragraph"> <p>The intent of this API was (originally at least) to provide a fallback whereby actions could at least be able to perform this validation programmatically (eg in their own <a href="rgcms.html#_rgcms_methods_prefixes_validate"><code>validate&#8230;&#8203;()</code></a> or <a href="rgcms.html#_rgcms_methods_prefixes_disable"><code>disable&#8230;&#8203;()</code></a> methods. However, this feature should be considered experimental; your mileage may vary.</p> </div> </div> <div class="sect3"> <h4 id="_rgsvc_api_DomainObjectContainer_properties-api">2.8.8. Properties API</h4> <div class="paragraph"> <p>The properties API allows domain objects to read the configuration properties aggregated from the various <a href="rgcfg.html#_rgcfg_configuration-files">configuration files</a>.</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="annotation">@Programmatic</span> <span class="predefined-type">String</span> getProperty(<span class="predefined-type">String</span> name); <i class="conum" data-value="1"></i><b>(1)</b> <span class="annotation">@Programmatic</span> <span class="predefined-type">String</span> getProperty(<span class="predefined-type">String</span> name, <span class="predefined-type">String</span> defaultValue); <i class="conum" data-value="2"></i><b>(2)</b> <span class="annotation">@Programmatic</span> <span class="predefined-type">List</span>&lt;<span class="predefined-type">String</span>&gt; getPropertyNames(); <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 the configuration property with the specified name; else return null.</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>Return the configuration property with the specified name; if it doesn&#8217;t exist then return the specified default value.</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>Return the names of all the available properties.</td> </tr> </table> </div> <div class="paragraph"> <p>For example, here&#8217;s a fictitious service that might wrap <a href="http://twitter4j.org/en/configuration.html">Twitter4J</a>. say:</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">TweetService</span> { <span class="annotation">@Programmatic</span> <span class="annotation">@PostConstruct</span> <span class="directive">public</span> <span class="type">void</span> init() { <span class="local-variable">this</span>.oauthConsumerKey = container.getProperty(<span class="string"><span class="delimiter">&quot;</span><span class="content">tweetservice.oauth.consumerKey</span><span class="delimiter">&quot;</span></span>); <span class="local-variable">this</span>.oauthConsumerSecret = container.getProperty(<span class="string"><span class="delimiter">&quot;</span><span class="content">tweetservice.oauth.consumerSecret</span><span class="delimiter">&quot;</span></span>); <span class="local-variable">this</span>.oauthAccessToken = container.getProperty(<span class="string"><span class="delimiter">&quot;</span><span class="content">tweetservice.oauth.accessToken</span><span class="delimiter">&quot;</span></span>); <span class="local-variable">this</span>.oauthAccessTokenSecret = container.getProperty(<span class="string"><span class="delimiter">&quot;</span><span class="content">tweetservice.oauth.accessTokenSecret</span><span class="delimiter">&quot;</span></span>); } ... <span class="annotation">@Inject</span> DomainObjectContainer container; }</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 you <em>do</em> have a domain service that needs to access properties, then note that an alternative is to define a <a href="rgant.html#_rgant-PostConstruct"><code>@PostConstruct</code></a> method and pass in a <code>Map&lt;String,String&gt;</code> of properties. The two techniques are almost identical; it&#8217;s mostly a matter of taste.</p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_rgsvc_api_DomainObjectContainer_services-api">2.8.9. Services API</h4> <div class="paragraph"> <p>The services API allows your domain objects to programmatically inject services into arbitrary objects, as well as to look up services by type:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">interface</span> <span class="class">DomainObjectContainer</span> { <span class="annotation">@Programmatic</span> &lt;T&gt; T injectServicesInto(<span class="directive">final</span> T domainObject); <i class="conum" data-value="1"></i><b>(1)</b> <span class="annotation">@Programmatic</span> &lt;T&gt; T lookupService(<span class="predefined-type">Class</span>&lt;T&gt; service); <i class="conum" data-value="2"></i><b>(2)</b> <span class="annotation">@Programmatic</span> &lt;T&gt; <span class="predefined-type">Iterable</span>&lt;T&gt; lookupServices(<span class="predefined-type">Class</span>&lt;T&gt; service); <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>injects services into domain object; used extensively internally by the framework (eg to inject to other services, or to entities, or integration test instances, or fixture scripts). Service injection is done automatically if objects are created using <code>#newTransientInstance()</code>, described <a href="#_rgsvc_api_DomainObjectContainer_object-creation-api">above</a></td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>returns the first registered service that implements the specified class</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>returns an <code>Iterable</code> in order to iterate over all registered services that implement the specified class</td> </tr> </table> </div> <div class="paragraph"> <p>The primary use case is to instantiate domain objects using a regular constructor ("new is the new new") rather than using the <a href="#_rgsvc_api_DomainObjectContainer_object-creation-api"><code>#newTransientInstance()</code></a> API, and then using the <code>#injectServicesInto(&#8230;&#8203;)</code> API to set up any dependencies.</p> </div> <div class="paragraph"> <p>For example:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Customer cust = container.injectServicesInto( <span class="keyword">new</span> Customer()); cust.setFirstName(<span class="string"><span class="delimiter">&quot;</span><span class="content">Freddie</span><span class="delimiter">&quot;</span></span>); cust.setLastName(<span class="string"><span class="delimiter">&quot;</span><span class="content">Mercury</span><span class="delimiter">&quot;</span></span>); container.persist(cust);</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_registering_the_service_5">2.8.10. Registering the Service</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' core implementation of <code>DomainObjectContainer</code> service is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_EmailService">2.9. <code>EmailService</code></h3> <div class="paragraph"> <p>The <code>EmailService</code> provides the ability to send HTML emails, with attachments, to one or more recipients.</p> </div> <div class="paragraph"> <p>Apache Isis provides a default implementation to send emails using an external SMTP provider. Note that this must be configured (using a number of configuration properties) before it can be used. The that sends email as an HTML message, using an external SMTP provider.</p> </div> <div class="sect3"> <h4 id="_api_implementation_7">2.9.1. API &amp; Implementation</h4> <div class="paragraph"> <p>The API for the service 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">interface</span> <span class="class">EmailService</span> { <span class="annotation">@Programmatic</span> <span class="type">boolean</span> send( <i class="conum" data-value="1"></i><b>(1)</b> <span class="predefined-type">List</span>&lt;<span class="predefined-type">String</span>&gt; to, <span class="predefined-type">List</span>&lt;<span class="predefined-type">String</span>&gt; cc, <span class="predefined-type">List</span>&lt;<span class="predefined-type">String</span>&gt; bcc, <i class="conum" data-value="2"></i><b>(2)</b> <span class="predefined-type">String</span> subject, <span class="predefined-type">String</span> body, <i class="conum" data-value="3"></i><b>(3)</b> <span class="predefined-type">DataSource</span>... attachments); <span class="annotation">@Programmatic</span> <span class="type">boolean</span> isConfigured(); <i class="conum" data-value="4"></i><b>(4)</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 the main API to send the email (and optional attachments). Will return <code>false</code> if failed to send</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>pass either <code>null</code> or <code>Collections.emptyList()</code> if not required</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>should be HTML text</td> </tr> <tr> <td><i class="conum" data-value="4"></i><b>4</b></td> <td>indicates whether the implementation was configured and initialized correctly. If this returns <code>false</code> then any attempt to call <code>send(&#8230;&#8203;)</code> will fail.</td> </tr> </table> </div> <div class="paragraph"> <p>As noted in the introduction, the core framework provides a default implementation (<code>EmailServiceDefault</code>) that sends email as an HTML message, using an external SMTP provider.</p> </div> </div> <div class="sect3"> <h4 id="_configuration">2.9.2. Configuration</h4> <div class="paragraph"> <p>To use this service the following properties must be configured:</p> </div> <div class="ulist"> <ul> <li> <p><code>isis.service.email.sender.address</code></p> </li> <li> <p><code>isis.service.email.sender.password</code></p> </li> </ul> </div> <div class="paragraph"> <p>and these properties may optionally be configured (each has a default to use gmail, documented <a href="rgcfg.html#_rgcfg_configuring-core">here</a>):</p> </div> <div class="ulist"> <ul> <li> <p><code>isis.service.email.sender.hostname</code></p> </li> <li> <p><code>isis.service.email.port</code></p> </li> <li> <p><code>isis.service.email.tls.enabled</code></p> </li> </ul> </div> <div class="paragraph"> <p>These configuration properties can be specified either in <code>isis.properties</code> or in an <a href="ugbtb.html#_ugbtb_deployment_externalized-configuration">external configuration file</a>.</p> </div> <div class="paragraph"> <p>If prototyping (that is, running the app using <code>org.apache.isis.WebServer</code>), the configuration properties can also be specified as system properties. For example, if you create a test email account on gmail, you can configure the service using:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="ini">-Disis.service.email.sender.address=xxx@gmail.com -Disis.service.email.sender.password=yyy</code></pre> </div> </div> <div class="paragraph"> <p>where "xxx" is the gmail user account and "yyy" is its password</p> </div> </div> <div class="sect3"> <h4 id="_alternative_implementations_2">2.9.3. Alternative Implementations</h4> <div class="paragraph"> <p>If you wish to write an alternative implementation, be aware that it should process the message body as HTML (as opposed to plain text or any other format).</p> </div> <div class="paragraph"> <p>Also, note that (unlike most Apache Isis domain services) the implementation is also instantiated and injected by Google Guice. This is because <code>EmailService</code> is used as part of the <a href="ugvw.html#_ugvw_features_user-registration">user registration</a> functionality and is used by Wicket pages that are accessed outside of the usual Apache Isis runtime. This implies a couple of additional constraints:</p> </div> <div class="ulist"> <ul> <li> <p>first, implementation class should also be annotated with <code>@com.google.inject.Singleton</code></p> </li> <li> <p>second, there may not be any Apache Isis session running. (If necessary, one can be created on the fly using <code>IsisContext.doInSession(&#8230;&#8203;)</code>)</p> </li> </ul> </div> <div class="paragraph"> <p>To ensure that your alternative implementation takes the place of the default implementation, register it explicitly in <code>isis.properties</code>.</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_services_4">2.9.4. Registering the Services</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' core implementation of <code>EmailService</code> service is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> <div class="sect3"> <h4 id="_related_services_3">2.9.5. Related Services</h4> <div class="paragraph"> <p>The email service is used by the <a href="#_rgsvc_spi_EmailNotificationService"><code>EmailNotificationService</code></a> which is, in turn, used by <a href="#_rgsvc_spi_UserRegistrationService"><code>UserRegistrationService</code></a>.</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_EventBusService">2.10. <code>EventBusService</code></h3> <div class="paragraph"> <p>The <code>EventBusService</code> allows domain objects to emit events to subscribing domain services using an in-memory event bus.</p> </div> <div class="paragraph"> <p>The primary user of the service is the framework itself, which automatically emit events for <a href="rgant.html#_rgant-Action_domainEvent">actions</a>, <a href="rgant.html#_rgant-Property_domainEvent">properties</a> and <a href="rgant.html#_rgant-Collection_domainEvent">collections</a>. Multiple events are generated:</p> </div> <div class="ulist"> <ul> <li> <p>when an object member is to be viewed, an event is fired; subscribers can veto (meaning that the member is hidden)</p> </li> <li> <p>when an object member is to be enabled, the same event instance is fired; subscribers can veto (meaning that the member is disabled, ie cannot be edited/invoked)</p> </li> <li> <p>when an object member is being validated, then a new event instance is fired; subscribers can veto (meaning that the candidate values/action arguments are rejected)</p> </li> <li> <p>when an object member is about to be changed, then the same event instance is fired; subscribers can perform pre-execution operations</p> </li> <li> <p>when an object member has been changed, then the same event instance is fired; subscribers can perform post-execution operations</p> </li> </ul> </div> <div class="paragraph"> <p>If a subscriber throws an exception in the first three steps, then the interaction is vetoed. If a subscriber throws an exception in the last two steps, then the transaction is aborted. For more on this topic, see <a href="rgant.html#_rgant-Action_domainEvent"><code>@Action#domainEvent()</code></a>, <a href="rgant.html#_rgant-Property_domainEvent"><code>@Property#domainEvent()</code></a> and <a href="rgant.html#_rgant-Collection_domainEvent"><code>@Collection#domainEvent()</code></a>.</p> </div> <div class="paragraph"> <p>It is also possible for domain objects to programmatically generate domain events. However the events are published, the primary use case is to decoupling interactions from one module/package/namespace and another.</p> </div> <div class="paragraph"> <p>Two implementations are available, using either <a href="https://code.google.com/p/guava-libraries/">Guava</a>'s <a href="https://code.google.com/p/guava-libraries/wiki/EventBusExplained"><code>EventBus</code></a>, or alternatively using the <a href="http://www.axonframework.org/">AxonFramework</a>'s <a href="http://www.axonframework.org/docs/2.4/single.html#d5e1489">SimpleEventBus</a>. It is also possible to plug in a custom implementation.</p> </div> <div class="sect3"> <h4 id="_rgsvc_api_EventBusService_api-and-implementation">2.10.1. API &amp; Implementation</h4> <div class="paragraph"> <p>The API defined by <code>EventBusService</code> 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">EventBusService</span> { <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="type">void</span> post(<span class="predefined-type">Object</span> event) { ... } <i class="conum" data-value="1"></i><b>(1)</b> <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="type">void</span> register(<span class="directive">final</span> <span class="predefined-type">Object</span> domainService) { ... } <i class="conum" data-value="2"></i><b>(2)</b> <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="type">void</span> unregister(<span class="directive">final</span> <span class="predefined-type">Object</span> domainService) { ... } <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>posts the event onto event bus</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>allows domain services to register themselves. This should be done in their <a href="rgant.html#_rgant-PostConstruct"><code>@PostConstruct</code></a> initialization method (for both singleton and <a href="rgant.html#_rgant-RequestScoped"><code>@RequestScoped</code></a> domain services.</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>exists for symmetry, but need never be called (it is in fact deliberately a no-op).</td> </tr> </table> </div> <div class="paragraph"> <p>Isis provides a default implementation of the service, <code>o.a.i.objectstore.jdo.datanucleus.service.eventbus.EventBusServiceJdo</code>.</p> </div> </div> <div class="sect3"> <h4 id="_registering_subscribers">2.10.2. Registering Subscribers</h4> <div class="paragraph"> <p>The <code>register()</code> method should be called in the <a href="rgant.html#_rgant-PostConstruct"><code>@PostConstruct</code></a> lifecycle method. It is valid and probably the least confusing to readers to also "unregister" in the <a href="rgant.html#_rgant-PreDestroy"><code>@PreDestroy</code></a> lifecycle method (though as noted <a href="#_rgsvc_api_EventBusService_api-and-implementation">above</a>, unregistering is actually a no-op).</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">@DomainService</span>(nature=NatureOfService.DOMAIN) <i class="conum" data-value="1"></i><b>(1)</b> <span class="annotation">@DomainServiceLayout</span>( menuOrder=<span class="string"><span class="delimiter">&quot;</span><span class="content">1</span><span class="delimiter">&quot;</span></span>) <i class="conum" data-value="2"></i><b>(2)</b> <span class="directive">public</span> <span class="type">class</span> <span class="class">MySubscribingDomainService</span> { <span class="annotation">@PostConstruct</span> <span class="directive">public</span> <span class="type">void</span> postConstruct() { eventBusService.register(<span class="local-variable">this</span>); <i class="conum" data-value="3"></i><b>(3)</b> } <span class="annotation">@PreDestroy</span> <span class="directive">public</span> <span class="type">void</span> preDestroy() { eventBusService.unregister(<span class="local-variable">this</span>); <i class="conum" data-value="4"></i><b>(4)</b> } ... <span class="annotation">@javax</span>.inject.Inject EventBusService eventBusService; }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>subscribers are typically not visible in the UI, so specify a <code>DOMAIN</code> nature</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>It&#8217;s important that subscribers register before any domain services that might emit events on the event bus service. For example, the (non-ASF) <a href="http://github.com/isisaddons/isis-module-security">Isis addons' security</a> module provides a domain service that automatically seeds certain domain entities; these will generate <a href="rgcms.html#_rgcms_classes_lifecycleevent">lifecycle events</a> and so any subscribers must be registered before such seed services. The easiest way to do this is to use the <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> attribute.</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>register with the event bus service during <a href="rgant.html#_rgant-PostConstruct"><code>@PostConstruct</code></a> initialization</td> </tr> <tr> <td><i class="conum" data-value="4"></i><b>4</b></td> <td>corresponding deregister when shutting down</td> </tr> </table> </div> <div class="paragraph"> <p>This works for both singleton (application-scoped) and also <a href="rgant.html#_rgant-RequestScoped"><code>@RequestScoped</code></a> domain services.</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 <a href="rgcms.html#_rgcms_classes_super_AbstractSubscriber"><code>AbstractSubscriber</code></a> class automatically performs this registration. As a convenience, it is also annotated with the <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> attribute.</p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_annotating_members">2.10.3. Annotating Members</h4> <div class="paragraph"> <p>As discussed in the introduction, the framework will automatically emit domain events for all of the object members (actions, properties or collections) of an object whenever that object is rendered or (more generally) interacted with.</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">Customer</span> { <span class="annotation">@Action</span> <span class="directive">public</span> Customer placeOrder(Product product, <span class="annotation">@ParameterLayout</span>(named=<span class="string"><span class="delimiter">&quot;</span><span class="content">Quantity</span><span class="delimiter">&quot;</span></span>) <span class="type">int</span> qty) { ... } ... }</code></pre> </div> </div> <div class="paragraph"> <p>will propagate an instance of the default <code>o.a.i.applib.services.eventbus.ActionDomainEvent.Default</code> class. If using the Guava event bus this can be subscribed to using:</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">MySubscribingDomainService</span> <span class="annotation">@Programmatic</span> <span class="annotation">@com</span>.google.common.eventbus.Subscribe <span class="directive">public</span> <span class="type">void</span> on(ActionDomainEvent ev) { ... } ... }</code></pre> </div> </div> <div class="paragraph"> <p>or if using Axonframework, the subscriber uses a different annotation:</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">MySubscribingDomainService</span> <span class="annotation">@Programmatic</span> <span class="annotation">@org</span>.axonframework.eventhandling.annotation.EventHandle <span class="directive">public</span> <span class="type">void</span> on(ActionDomainEvent ev) { ... } ... }</code></pre> </div> </div> <div class="paragraph"> <p>More commonly though you will probably want to emit domain events of a specific subtype. As a slightly more interesting example, suppose in a library domain that a <code>LibraryMember</code> wants to leave the library. A letter should be sent out detailing any books that they still have out on loan:</p> </div> <div class="paragraph"> <p>In the <code>LibraryMember</code> class, we publish the event by way of an annotation:</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="annotation">@Action</span>(domainEvent=LibraryMemberLeaveEvent.class) <i class="conum" data-value="1"></i><b>(1)</b> <span class="directive">public</span> <span class="type">void</span> leave() { ... } ... }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td><code>LibraryMemberLeaveEvent</code> is a subclass of <code>o.a.i.applib.eventbus.ActionDomainEvent</code>. The topic of subclassing is discussed in more detail <a href="#_rgsvc_api_EventBusService_event-hierarchy">below</a>.</td> </tr> </table> </div> <div class="paragraph"> <p>Meanwhile, in the <code>BookRepository</code> domain service, we subscribe to the event and act upon it. 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">BookRepository</span> { <span class="annotation">@Programmatic</span> <span class="annotation">@com</span>.google.common.eventbus.Subscribe <span class="directive">public</span> <span class="type">void</span> onLibraryMemberLeaving(LibraryMemberLeaveEvent e) { LibraryMember lm = e.getLibraryMember(); <span class="predefined-type">List</span>&lt;<span class="predefined-type">Book</span>&gt; lentBooks = findBooksOnLoanFor(lm); <span class="keyword">if</span>(!lentBooks.isEmpty()) { sendLetter(lm, lentBooks); } } ... }</code></pre> </div> </div> <div class="paragraph"> <p>This design allows the <code>libraryMember</code> module to be decoupled from the <code>book</code> module.</p> </div> </div> <div class="sect3"> <h4 id="_rgsvc_api_EventBusService_event-hierarchy">2.10.4. Event hierarchy</h4> <div class="paragraph"> <p>By creating domain event subtypes we can be more semantically precise and in turn providesmore flexibility for subscribers: they can choose whether to be broadly applicable (by subscribing to a superclass) or to be tightly focussed (by subscribing to a subclass).</p> </div> <div class="paragraph"> <p>We recommend that you define event classes at (up to) four scopes:</p> </div> <div class="ulist"> <ul> <li> <p>at the top "global" scope is the Apache Isis-defined <code>o.a.i.applib.event.ActionDomainEvent</code></p> </li> <li> <p>for the "module" scope, create a static class to represent the module itself, and creating nested classes within</p> </li> <li> <p>for each "class" scope, create a nested static event class in the domain object&#8217;s class for all of the domain object&#8217;s actions</p> </li> <li> <p>for each "action" scope, create a nested static event class for that action, inheriting from the "domain object" class.</p> </li> </ul> </div> <div class="paragraph"> <p>To put all that into code; at the module level we can define:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">package</span> <span class="namespace">com.mycompany.modules.libmem</span>; ... public <span class="directive">static</span> <span class="type">class</span> <span class="class">LibMemModule</span> { <span class="directive">private</span> LibMemModule() {} <span class="directive">public</span> <span class="directive">abstract</span> <span class="directive">static</span> <span class="type">class</span> <span class="class">ActionDomainEvent</span>&lt;S&gt; <span class="directive">extends</span> org.apache.isis.applib.event.ActionDomainEvent&lt;S&gt; {} ... <i class="conum" data-value="1"></i><b>(1)</b> public <span class="directive">abstract</span> <span class="directive">static</span> <span class="type">class</span> <span class="class">PropertyDomainEvent</span>&lt;S,T&gt; <span class="directive">extends</span> org.apache.isis.applib.event.PropertyDomainEvent&lt;S,T&gt; {} <span class="directive">public</span> <span class="directive">abstract</span> <span class="directive">static</span> <span class="type">class</span> <span class="class">CollectionDomainEvent</span>&lt;S,E&gt; <span class="directive">extends</span> org.apache.isis.applib.event.CollectionDomainEvent&lt;S,E&gt; {} }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>similar events for properties and collections should also be defined</td> </tr> </table> </div> <div class="paragraph"> <p>For the class-level we can define:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="directive">static</span> <span class="type">class</span> <span class="class">LibraryMember</span> { <span class="directive">public</span> <span class="directive">abstract</span> <span class="directive">static</span> <span class="type">class</span> <span class="class">ActionDomainEvent</span> <span class="directive">extends</span> LibMemModule.ActionDomainEvent&lt;LibraryMember&gt; { } ... <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>similar events for properties and collections should also be defined</td> </tr> </table> </div> <div class="paragraph"> <p>and finally at the action level we can define:</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">public</span> <span class="directive">static</span> <span class="type">class</span> <span class="class">LeaveEvent</span> <span class="directive">extends</span> LibraryMember.ActionDomainEvent { } <span class="annotation">@Action</span>(domainEvent=LeaveEvent.class) <span class="directive">public</span> <span class="type">void</span> leave() { ... } ... }</code></pre> </div> </div> <div class="paragraph"> <p>The subscriber can subscribe either to the general superclass (as before), or to any of the classes in the hierarchy.</p> </div> <div class="sect4"> <h5 id="_variation_for_contributing_services">Variation (for contributing services)</h5> <div class="paragraph"> <p>A slight variation on this is to not fix the generic parameter at the class level, ie:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="directive">static</span> <span class="type">class</span> <span class="class">LibraryMember</span> { <span class="directive">public</span> <span class="directive">abstract</span> <span class="directive">static</span> <span class="type">class</span> <span class="class">ActionDomainEvent</span>&lt;S&gt; <span class="directive">extends</span> LibMemModule.ActionDomainEvent&lt;S&gt; { } ... }</code></pre> </div> </div> <div class="paragraph"> <p>and instead parameterize down at the action level:</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">public</span> <span class="directive">static</span> <span class="type">class</span> <span class="class">LeaveEvent</span> <span class="directive">extends</span> LibraryMember.ActionDomainEvent&lt;LibraryMember&gt; { } <i class="conum" data-value="1"></i><b>(1)</b> } <span class="annotation">@Action</span>(domainEvent=LeaveEvent.class) <span class="directive">public</span> <span class="type">void</span> leave() { ... } ... }</code></pre> </div> </div> <div class="paragraph"> <p>This then allows for other classes - in particular domain services contributing members - to also inherit from the class-level domain events.</p> </div> </div> </div> <div class="sect3"> <h4 id="_programmatic_posting">2.10.5. Programmatic posting</h4> <div class="paragraph"> <p>To programmatically post an event, simply call <code>#post()</code>.</p> </div> <div class="paragraph"> <p>The <code>LibraryMember</code> example described above could for example be rewritten into:</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> { ... public <span class="type">void</span> leave() { ... eventBusService.post(<span class="keyword">new</span> LibraryMember.LeaveEvent(...)); <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>LibraryMember.LeaveEvent</code> could be <em>any</em> class, not just a subclass of <code>o.a.i.applib.event.ActionDomainEvent</code>.</td> </tr> </table> </div> <div class="paragraph"> <p>In practice we suspect there will be few cases where the programmatic approach is required rather than the declarative approach afforded by <a href="rgant.html#_rgant-Action_domainEvent"><code>@Action#domainEvent()</code></a> et al.</p> </div> </div> <div class="sect3"> <h4 id="_using_code_wrapperfactory_code">2.10.6. Using <code>WrapperFactory</code></h4> <div class="paragraph"> <p>An alternative way to cause events to be posted is through the <a href="#_rgsvc_api_WrapperFactory"><code>WrapperFactory</code></a>. This is useful when you wish to enforce a (lack-of-) trust boundary between the caller and the callee.</p> </div> <div class="paragraph"> <p>For example, suppose that <code>Customer#placeOrder(&#8230;&#8203;)</code> emits a <code>PlaceOrderEvent</code>, which is subscribed to by a <code>ReserveStockSubscriber</code>. This subscriber in turn calls <code>StockManagementService#reserveStock(&#8230;&#8203;)</code>. Any business rules on <code>#reserveStock(&#8230;&#8203;)</code> should be enforced.</p> </div> <div class="paragraph"> <p>In the <code>ReserveStockSubscriber</code>, we therefore use the <code>WrapperFactory</code>:</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">ReserveStockSubscriber</span> { <span class="annotation">@Programmatic</span> <span class="annotation">@Subscribe</span> <span class="directive">public</span> <span class="type">void</span> on(Customer.PlaceOrderEvent ev) { wrapperFactory.wrap(stockManagementService) .reserveStock(ev.getProduct(), ev.getQuantity()); } ... <span class="annotation">@Inject</span> StockManagementService stockManagementService; <span class="annotation">@Inject</span> WrapperFactory wrapperFactory; }</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_implementation_spi">2.10.7. Implementation SPI</h4> <div class="paragraph"> <p>The implementation of <code>EventBusService</code> provided by Apache Isis will by default use <a href="https://code.google.com/p/guava-libraries/">Guava</a>'s <a href="https://code.google.com/p/guava-libraries/wiki/EventBusExplained"><code>EventBus</code></a> as the underlying in-memory event bus. Alternatively the <a href="http://www.axonframework.org/">AxonFramework</a>'s <a href="http://www.axonframework.org/docs/2.4/single.html#d5e1489">SimpleEventBus</a> can be used. Which is used is specified through configuration property (described <a href="#_rgsvc_api_EventBusService_Configuration">below</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="title">Guava vs Axon, which to use?</div> <div class="paragraph"> <p>Guava actually queues up events; they are not guaranteed to be dispatched immediately. This generally is not problem, but can be for cases where the subscriber may in turn want to post its own events (using <a href="#_rgsvc_api_WrapperFactory"><code>WrapperFactory</code></a>).</p> </div> <div class="paragraph"> <p>The Axon <code>SimpleEventBus</code>-based implementation on the other hand is fully synchronous; events are dispatched as soon as they are posted. This works well in all scenarios (that we have tested).</p> </div> </td> </tr> </table> </div> <div class="paragraph"> <p>It is also possible to use some other implementation.</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">EventBusImplementation</span> { <span class="type">void</span> register(<span class="predefined-type">Object</span> domainService); <span class="type">void</span> unregister(<span class="predefined-type">Object</span> domainService); <span class="type">void</span> post(<span class="predefined-type">Object</span> event); }</code></pre> </div> </div> <div class="paragraph"> <p>As is probably obvious, the <code>EventBusService</code> just delegates down to these method calls when its own similarly named methods are called.</p> </div> <div class="paragraph"> <p>If you do provide your own implementation of this SPI, be aware that your subscribers will need to use whatever convention is required (eg different annotations) such that the events are correctly routed through to your subscribers.</p> </div> </div> <div class="sect3"> <h4 id="_rgsvc_api_EventBusService_Configuration">2.10.8. Configuration</h4> <div class="paragraph"> <p>The implementation of <code>EventBusService</code> provided by Apache Isis will by default use <a href="https://code.google.com/p/guava-libraries/">Guava</a>'s <a href="https://code.google.com/p/guava-libraries/wiki/EventBusExplained"><code>EventBus</code></a> as the underlying in-memory event bus. Alternatively the <a href="http://www.axonframework.org/">AxonFramework</a>'s <a href="http://www.axonframework.org/docs/2.4/single.html#d5e1489">SimpleEventBus</a> can be used.</p> </div> <div class="paragraph"> <p>To specify which, add the <a href="rgcfg.html#_rgcfg_configuring-core">configuration property</a> <code>isis.services.eventbus.implementation</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="ini">isis.services.eventbus.implementation=guava</code></pre> </div> </div> <div class="paragraph"> <p>or</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="ini">isis.services.eventbus.implementation=axon</code></pre> </div> </div> <div class="paragraph"> <p>If you have written your own implementation of the <code>EventBusServiceImplementation</code> SPI, then specify instead its fully-qualified class name:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="ini">isis.services.eventbus.implementation=com.mycompany.isis.MyEventBusServiceImplementation</code></pre> </div> </div> <div class="paragraph"> <p>In addition, there is one further configuration property, whether to allow "late registration":</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="ini">isis.services.eventbus.allowLateRegistration=false</code></pre> </div> </div> <div class="paragraph"> <p>Late registration refers to the idea that a domain service can register itself with the <code>EventBusService</code> after events have been posted. Since domain services are set up at boot time, this almost certainly constitutes a bug in the code and so by default late registration is <em>not</em> allowed. Setting the above property to <code>true</code> disables this check.</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_services_5">2.10.9. Registering the Services</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' core implementation of <code>EventBusService</code> service is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> <div class="sect3"> <h4 id="_related_services_4">2.10.10. Related Services</h4> <div class="paragraph"> <p>The <code>EventBusService</code> is intended for fine-grained publish/subscribe for object-to-object interactions within an Apache Isis domain object model. The event propagation is strictly in-memory, and there are no restrictions on the object acting as the event (it need not be serializable, for example).</p> </div> <div class="paragraph"> <p>The <a href="#_rgsvc_spi_PublishingService"><code>PublishingService</code></a> meanwhile is intended for coarse-grained publish/subscribe for system-to-system interactions, from Apache Isis to some other system. Here the only events published are those that action invocations (for actions annotated with <a href="rgant.html#_rgant-Action_publishing"><code>@Action#publishing()</code></a>) and of changed objects (for objects annotated with <a href="rgant.html#_rgant-DomainObject_publishing"><code>@DomainObject#publishing()</code></a>).</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_FixtureScriptsDefault">2.11. <code>FixtureScriptsDefault</code></h3> <div class="paragraph"> <p>The <code>FixtureScriptsDefault</code> service provides the ability to execute <a href="ugtst.html#_ugtst_fixture-scripts_api-and-usage">fixture scripts</a> .</p> </div> <div class="paragraph"> <p>The service extends from the <a href="rgcms.html#_rgcms_classes_super_FixtureScripts"><code>FixtureScripts</code></a>, and is only instantiated by the framework if there no custom implementation of <code>FixtureScripts</code> has been otherwise provided; in other words it is a fallback.</p> </div> <div class="paragraph"> <p>If this service is instantiated (as a fallback) then it uses the <a href="#_rgsvc_spi_FixtureScriptsSpecificationProvider"><code>FixtureScriptsSpecificationProvider</code></a> to obtain a <code>FixtureScriptsSpecification</code>. This configures this service, telling it which package to search for <code>FixtureScript</code> classes, how to execute those classes, and hints that influence the UI.</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>We recommend using <a href="#_rgsvc_spi_FixtureScriptsSpecificationProvider"><code>FixtureScriptsSpecificationProvider</code></a> rather than subclassing <a href="rgcms.html#_rgcms_classes_super_FixtureScripts"><code>FixtureScripts</code></a>.</p> </div> </td> </tr> </table> </div> <div class="sect3"> <h4 id="_api_implementation_8">2.11.1. API &amp; Implementation</h4> <div class="paragraph"> <p>The API for the service 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">FixtureScriptsDefault</span> ... { <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="predefined-type">List</span>&lt;FixtureResult&gt; runFixtureScript( FixtureScript fixtureScript, <span class="predefined-type">String</span> parameters) { ... } }</code></pre> </div> </div> <div class="olist lowerroman"> <ol class="lowerroman" type="i"> <li> <p>in other words the same as <a href="rgcms.html#_rgcms_classes_super_FixtureScripts"><code>FixtureScripts</code></a> superclass that it inherits from.</p> </li> </ol> </div> </div> <div class="sect3"> <h4 id="_configuration_2">2.11.2. Configuration</h4> <div class="paragraph"> <p>As noted in the introduction, this service is only instantiated if there is no other implementation of <code>FixtureScripts</code> available on the classpath.</p> </div> <div class="paragraph"> <p>If an instance of <code>FixtureScriptsSpecificationProvider</code> is available on the classpath, then the service will be visible in the UI (assuming <a href="rgcfg.html#_rgcfg_deployment-types">prototype mode</a>). Otherwise the service will be available only to be injected and invoked programmatically.</p> </div> </div> <div class="sect3"> <h4 id="_related_services_5">2.11.3. Related Services</h4> <div class="paragraph"> <p>The service interacts with <a href="#_rgsvc_spi_FixtureScriptsSpecificationProvider"><code>FixtureScriptsSpecificationProvider</code></a>.</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_GuiceBeanProvider">2.12. <code>GuiceBeanProvider</code></h3> <div class="paragraph"> <p>The <code>GuiceBeanProvider</code> domain service acts as a bridge between Apache Isis' <a href="ugvw.html">Wicket viewer</a> internal bootstrapping using <a href="https://github.com/google/guice">Google Guice</a>.</p> </div> <div class="paragraph"> <p>This service operates at a very low-level, and you are unlikely to have a need for it. It is used internally by the framework, in the default implementation of the <a href="#_rgsvc_api_DeepLinkService"><code>DeepLinkService</code></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>Currently Apache Isis uses a combination of Guice (within the Wicket viewer only) and a home-grown dependency injection framework. In future versions we intended to refactor the framework to use CDI throughout. At that time this service is likely to become redundant because we will allow any of the internal components of Apache Isis to be injected into your domain object code.</p> </div> </td> </tr> </table> </div> <div class="sect3"> <h4 id="_api_implementation_9">2.12.1. API &amp; Implementation</h4> <div class="paragraph"> <p>The API defined by this service 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">interface</span> <span class="class">GuiceBeanProvider</span> { <span class="annotation">@Programmatic</span> &lt;T&gt; T lookup(<span class="predefined-type">Class</span>&lt;T&gt; beanType); <span class="annotation">@Programmatic</span> &lt;T&gt; T lookup(<span class="predefined-type">Class</span>&lt;T&gt; beanType, <span class="directive">final</span> <span class="predefined-type">Annotation</span> qualifier); }</code></pre> </div> </div> <div class="paragraph"> <p>The <a href="ugvw.html">Wicket viewer</a> this provides an implementation of this service.</p> </div> </div> <div class="sect3"> <h4 id="_usage_5">2.12.2. Usage</h4> <div class="paragraph"> <p>Using the <a href="ugvw.html">Wicket viewer</a> requires subclassing of <code>IsisWicketApplication</code>. In the subclass it is commonplace to override <code>newIsisWicketModule()</code>, for example:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Override</span> <span class="directive">protected</span> Module newIsisWicketModule() { <span class="directive">final</span> Module isisDefaults = <span class="local-variable">super</span>.newIsisWicketModule(); <span class="directive">final</span> Module overrides = <span class="keyword">new</span> AbstractModule() { <span class="annotation">@Override</span> <span class="directive">protected</span> <span class="type">void</span> configure() { bind(<span class="predefined-type">String</span>.class).annotatedWith(Names.named(<span class="string"><span class="delimiter">&quot;</span><span class="content">applicationName</span><span class="delimiter">&quot;</span></span>)) .toInstance(<span class="string"><span class="delimiter">&quot;</span><span class="content">ToDo App</span><span class="delimiter">&quot;</span></span>); bind(<span class="predefined-type">String</span>.class).annotatedWith(Names.named(<span class="string"><span class="delimiter">&quot;</span><span class="content">applicationCss</span><span class="delimiter">&quot;</span></span>)) .toInstance(<span class="string"><span class="delimiter">&quot;</span><span class="content">css/application.css</span><span class="delimiter">&quot;</span></span>); bind(<span class="predefined-type">String</span>.class).annotatedWith(Names.named(<span class="string"><span class="delimiter">&quot;</span><span class="content">applicationJs</span><span class="delimiter">&quot;</span></span>)) .toInstance(<span class="string"><span class="delimiter">&quot;</span><span class="content">scripts/application.js</span><span class="delimiter">&quot;</span></span>); ... } }; <span class="keyword">return</span> Modules.override(isisDefaults).with(overrides); }</code></pre> </div> </div> <div class="paragraph"> <p>This "module" is in fact a Guice module, and so the <code>GuiceBeanProvider</code> service can be used to lookup any of the components bound into it.</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">SomeDomainObject</span> { <span class="directive">private</span> <span class="predefined-type">String</span> lookupApplicationName() { <span class="keyword">return</span> guiceBeanProvider.lookup(<span class="predefined-type">String</span>.class, Names.named(<span class="string"><span class="delimiter">&quot;</span><span class="content">applicationName</span><span class="delimiter">&quot;</span></span>)); } <span class="annotation">@Inject</span> GuiceBeanProvider guiceBeanProvider; }</code></pre> </div> </div> <div class="paragraph"> <p>should return "ToDo App".</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_services_6">2.12.3. Registering the Services</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#<em>rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>), _and</em> that the <a href="ugvw.html">Wicket viewer</a> is being used, then an implementation of <code>GuiceBeanProvider</code> is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_HomePageProviderService">2.13. <code>HomePageProviderService</code></h3> <div class="paragraph"> <p>This service simply provides access to the home page object (if any) that is returned from the domain service action annotated with <a href="rgant.html#_rgant_HomePage"><code>@HomePage</code></a>.</p> </div> <div class="paragraph"> <p>It is originally introduced to support the default implementation of <a href="#_rgsvc_spi_RoutingService"><code>RoutingService</code></a>, but was factored out to support alternative implementations of that service (and may be useful for other use cases).</p> </div> <div class="sect3"> <h4 id="_api_implementation_10">2.13.1. API &amp; Implementation</h4> <div class="paragraph"> <p>The API defined by <code>HomePageProviderService</code> is:</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">interface</span> <span class="class">HomePageProviderService</span> { <span class="annotation">@Programmatic</span> <span class="predefined-type">Object</span> homePage(); }</code></pre> </div> </div> <div class="paragraph"> <p>The default implementation is provided by <code>o.a.i.core.runtime.services.homepage.HomePageProviderServiceDefault</code>.</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_service_6">2.13.2. Registering the Service</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' core implementation of <code>HomePageProviderService</code> is automatically registered (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_IsisJdoSupport">2.14. <code>IsisJdoSupport</code></h3> <div class="paragraph"> <p>The <code>IsisJdoSupport</code> service provides a number of general purpose methods for working with the JDO/DataNucleus objectstore. In general these act at a lower-level of abstraction than the APIs normally used (specifically, those of <a href="#_rgsvc_api_DomainObjectContainer"><code>DomainObjectContainer</code></a>), but nevertheless deal with some of the most common use cases. For service also provides access to the underlying JDO <code>PersistenceManager</code> for full control.</p> </div> <div class="paragraph"> <p>The following sections discuss the functionality provided by the service, broken out into categories.</p> </div> <div class="sect3"> <h4 id="_rgsvc_api_IsisJdoSupport_executing-sql">2.14.1. Executing SQL</h4> <div class="paragraph"> <p>You can use the <code>IsisJdoSupportService</code> to perform arbitrary SQL SELECTs or UPDATEs:</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">IsisJdoSupport</span> { <span class="annotation">@Programmatic</span> <span class="predefined-type">List</span>&lt;<span class="predefined-type">Map</span>&lt;<span class="predefined-type">String</span>, <span class="predefined-type">Object</span>&gt;&gt; executeSql(<span class="predefined-type">String</span> sql); <span class="annotation">@Programmatic</span> <span class="predefined-type">Integer</span> executeUpdate(<span class="predefined-type">String</span> sql); ... }</code></pre> </div> </div> <div class="paragraph"> <p>The <code>executeSql(&#8230;&#8203;)</code> method allows arbitrary SQL <code>SELECT</code> queries to be submitted:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="predefined-type">List</span>&lt;<span class="predefined-type">Map</span>&lt;<span class="predefined-type">String</span>, <span class="predefined-type">Object</span>&gt;&gt; results = isisJdoSupport.executeSql(<span class="string"><span class="delimiter">&quot;</span><span class="content">select * from custMgmt.customers</span><span class="delimiter">&quot;</span></span>);</code></pre> </div> </div> <div class="paragraph"> <p>The result set is automatically converted into a list of maps, where the map key is the column name.</p> </div> <div class="paragraph"> <p>In a similar manner, the <code>executeUpdate(&#8230;&#8203;)</code> allows arbitrary SQL <code>UPDATE</code>s to be performed.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="type">int</span> count = isisJdoSupport.executeUpdate(<span class="string"><span class="delimiter">&quot;</span><span class="content">select count(*) from custMgmt.customers);</span></span></code></pre> </div> </div> <div class="paragraph"> <p>The returned value is the number of rows updated.</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>As an alternative, consider using DataNucleus' <a href="http://www.datanucleus.org/products/accessplatform_4_0/jdo/jdoql_typesafe.html">type-safe JDO query API</a>, discussed <a href="#_rgsvc_api_IsisJdoSupport_type-safe-query-api">below</a>.</p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_rgsvc_api_IsisJdoSupport_type-safe-jdoql-queries">2.14.2. Type-safe JDOQL Queries</h4> <div class="paragraph"> <p>DataNucleus provides an <a href="http://www.datanucleus.org/products/accessplatform_4_0/jdo/jdoql_typesafe.html">extension to JDO</a>, so that JDOQL queries can be built up and executed using a set of type-safe classes.</p> </div> <div class="paragraph"> <p>The types in question for type safe queries are not the domain entities, but rather are companion "Q&#8230;&#8203;" query classes. These classes are generated dynamically by an <a href="https://www.jcp.org/en/jsr/detail?id=269">annotation processor</a> as a side-effect of compilation, one "Q&#8230;&#8203;" class for each of the <a href="rgant.html#_rgant-PersistenceCapable"><code>@PersistenceCapable</code></a> domain entity in your application. For example, a <code>ToDoItem</code> domain entity will give rise to a <code>QToDoItem</code> query class. These "Q&#8230;&#8203;" classes mirror the structure of domain entity, but expose properties that allow predicates to be built up for querying instances, as well as other functions in support of order by. group by and other clauses.</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>Most IDEs (including IntelliJ and Eclipse) enable annotation processing by default, as does Maven. The DataNucleus' <a href="http://www.datanucleus.org/products/accessplatform_4_0/jdo/jdoql_typesafe.html">documentation</a> offers some guidance on confirming that APT is enabled.</p> </div> </td> </tr> </table> </div> <div class="paragraph"> <p>The <code>IsisJdoSupport</code> service offers two methods at different levels of abstraction:</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">IsisJdoSupport</span> { <span class="annotation">@Programmatic</span> &lt;T&gt; <span class="predefined-type">List</span>&lt;T&gt; executeQuery(<span class="directive">final</span> <span class="predefined-type">Class</span>&lt;T&gt; cls, <span class="directive">final</span> BooleanExpression be); <span class="annotation">@Programmatic</span> &lt;T&gt; TypesafeQuery&lt;T&gt; newTypesafeQuery(<span class="predefined-type">Class</span>&lt;T&gt; cls); ... }</code></pre> </div> </div> <div class="paragraph"> <p>The <code>executeQuery(&#8230;&#8203;)</code> method supports the common case of obtaining a set of objects that meet some criteria, filtered using the provided <code>BooleanExpression</code>. To avoid memory leaks, the returned list is cloned and the underlying query closed.</p> </div> <div class="paragraph"> <p>For example, in the (non-ASF) <a href="http://github.com/isisaddons/isis-app-todoapp">Isis addons' todoapp</a> there is an implementation of <code>ToDoItemRepository</code> using type-safe queries. The following JDOQL:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="sql"><span class="class">SELECT</span> <span class="keyword">FROM</span> todoapp.dom.module.todoitem.ToDoItem <span class="keyword">WHERE</span> atPath.indexOf(:atPath) == <span class="integer">0</span> &amp;&amp; complete == :complete<span class="string"><span class="delimiter">&quot;</span></span></code></pre> </div> </div> <div class="paragraph"> <p>can be expressed using type-safe queries as follows:</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">List</span>&lt;ToDoItem&gt; findByAtPathAndCategory(<span class="directive">final</span> <span class="predefined-type">String</span> atPath, <span class="directive">final</span> Category category) { <span class="directive">final</span> QToDoItem q = QToDoItem.candidate(); <span class="keyword">return</span> isisJdoSupport.executeQuery(ToDoItem.class, q.atPath.eq(atPath).and( q.category.eq(category))); }</code></pre> </div> </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>You can find the full example of the JDOQL equivalent in the <a href="#_rgsvc_api_DomainObjectContainer_generic-repository-api"><code>DomainObjectContainer</code></a></p> </div> </td> </tr> </table> </div> <div class="paragraph"> <p>The <code>newTypesafeQuery(&#8230;&#8203;)</code> method is a lower-level API that allows a type safe query to be instantiated for most sophisticated querying, eg using group by or order by clauses. See the DataNucleus <a href="http://www.datanucleus.org/products/accessplatform_4_0/jdo/jdoql_typesafe.html">documentation</a> for full details of using this.</p> </div> <div class="paragraph"> <p>One thing to be aware of is that after the query has been executed, it should be closed, using <code>query.closeAll()</code>. If calling <code>query.executeList()</code> we also recommend cloning the resultant list first. The following utility method does both of these tasks:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="directive">private</span> <span class="directive">static</span> &lt;T&gt; <span class="predefined-type">List</span>&lt;T&gt; executeListAndClose(<span class="directive">final</span> TypesafeQuery&lt;T&gt; query) { <span class="directive">final</span> <span class="predefined-type">List</span>&lt;T&gt; elements = query.executeList(); <span class="directive">final</span> <span class="predefined-type">List</span>&lt;T&gt; list = Lists.newArrayList(elements); query.closeAll(); <span class="keyword">return</span> list; }</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_rgsvc_api_IsisJdoSupport_fixture-support">2.14.3. Fixture support</h4> <div class="paragraph"> <p>When writing <a href="ugtst.html#_ugtst_integ-test-support">integration tests</a> you&#8217;ll usually need to tear down some/all mutable transactional data before each test. One way to do that is to use the <code>executeUpdate(&#8230;&#8203;)</code> method described <a href="#_rgsvc_api_IsisJdoSupport_executing-sql">above</a>.</p> </div> <div class="paragraph"> <p>Alternatively, the <code>deleteAll(&#8230;&#8203;)</code> method will let your test delete all instances of a class without resorting to SQL:</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">IsisJdoSupport</span> { <span class="annotation">@Programmatic</span> <span class="type">void</span> deleteAll(<span class="predefined-type">Class</span>&lt;?&gt;... pcClasses); ... }</code></pre> </div> </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">TearDownAll</span> <span class="directive">extends</span> FixtureScriptAbstract { <span class="annotation">@Override</span> <span class="directive">protected</span> <span class="type">void</span> execute(<span class="directive">final</span> ExecutionContext ec) { isisJdoSupport.deleteAll(Order.class); isisJdoSupport.deleteAll(CustomerAddress.class); isisJdoSupport.deleteAll(Customer.class); } <span class="annotation">@Inject</span> IsisJdoSupport isisJdoSupport; }</code></pre> </div> </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>It can occasionally be the case that Apache Isis' internal adapter for the domain object is still in memory. JDO/DataNucleus seems to bump up the version of the object prior to its deletion, which under normal circumstances would cause Apache Isis to throw a concurrency exception. Therefore to prevent this from happening (ie to <em>force</em> the deletion of all instances), concurrency checking is temporarily disabled while this method is performed.</p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_rgsvc_api_IsisJdoSupport_reloading-entities">2.14.4. Reloading entities</h4> <div class="paragraph"> <p>An <a href="http://www.datanucleus.org/products/datanucleus/jdo/orm/relationships.html">(intentional) limitation</a> of JDO/DataNucleus is that persisting a child entity (in a 1:n bidirectional relationship) does not cause the parent&#8217;s collection to be updated.</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">IsisJdoSupport</span> { <span class="annotation">@Programmatic</span> &lt;T&gt; T refresh(T domainObject); <span class="annotation">@Programmatic</span> <span class="type">void</span> ensureLoaded(<span class="predefined-type">Collection</span>&lt;?&gt; collectionOfDomainObjects); ... }</code></pre> </div> </div> <div class="paragraph"> <p>The <code>refresh(T domainObject)</code> method can be used to reload the parent object (or indeed any object). Under the covers it uses the JDO <code>PersistenceManager#refresh(&#8230;&#8203;)</code> API.</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">@DomainService</span>(nature=NatureOfService.VIEW_CONTRIBUTIONS_ONLY) <span class="directive">public</span> <span class="type">class</span> <span class="class">OrderContributions</span> { <span class="directive">public</span> Order newOrder(<span class="directive">final</span> Customer customer) { Order order = newTransientInstance(Order.class); order.setCustomer(customer); container.persist(customer); container.flush(); <i class="conum" data-value="1"></i><b>(1)</b> isisJdoSupport.refresh(customer); <i class="conum" data-value="2"></i><b>(2)</b> <span class="keyword">return</span> order; } <span class="annotation">@Inject</span> DomainObjectContainer container; <span class="annotation">@Inject</span> IsisJdoSupport isisJdoSupport; }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>flush to database, ensuring that the database row corresponding to the <code>Order</code> exists in its <code>order</code> table.</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>reload the parent (customer) from the database, so that its collection of <code>Order</code>s is accurate.</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>The particular example that led to this method being added was a 1:m bidirectional relationship, analogous to <code>Customer 1&#8592;&#8594;* Order</code>. Persisting the child <code>Order</code> object did not cause the parent <code>Customer</code>'s collection of orders to be updated. In fact, JDO does not make any such guarantee to do so. Options are therefore either to maintain the collection in code, or to refresh the parent.</p> </div> </td> </tr> </table> </div> <div class="paragraph"> <p>The <code>ensureLoaded(&#8230;&#8203;)</code> method allows a collection of domain objects to be loaded from the database in a single hit. This can be valuable as a performance optimization to avoid multiple roundtrips to the database. Under the covers it uses the <code>PersistenceManager#retrieveAll(&#8230;&#8203;)</code> API.</p> </div> </div> <div class="sect3"> <h4 id="_rgsvc_api_IsisJdoSupport_jdo-persistencemanager">2.14.5. JDO <code>PersistenceManager</code></h4> <div class="paragraph"> <p>The functionality provided by <code>IsisJdoSupport</code> focus only on the most common use cases. If you require more flexibility than this, eg for dynamically constructed queries, then you can use the service to access the underlying JDO <code>PersistenceManager</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">IsisJdoSupport</span> { <span class="annotation">@Programmatic</span> PersistenceManager getJdoPersistenceManager(); ... }</code></pre> </div> </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="predefined-type">List</span>&lt;Order&gt; findOrders(...) { javax.jdo.PersistenceManager pm = isisJdoSupport.getPersistenceManager(); <span class="comment">// knock yourself out...</span> <span class="keyword">return</span> someListOfOrders; }</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_registering_the_services_7">2.14.6. Registering the Services</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' core implementation of <code>IsisJdoSupport</code> service is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_JaxbService">2.15. <code>JaxbService</code></h3> <div class="paragraph"> <p>The <code>JaxbService</code> allows instances of JAXB-annotated classes to be marshalled to XML and unmarshalled from XML back into domain objects.</p> </div> <div class="sect3"> <h4 id="_rgsvc_api_JaxbService_api-and-implementation">2.15.1. API &amp; Implementation</h4> <div class="paragraph"> <p>The API defined by <code>JaxbService</code> 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">interface</span> <span class="class">JaxbService</span> { <span class="annotation">@Programmatic</span> &lt;T&gt; T fromXml(<span class="predefined-type">Class</span>&lt;T&gt; domainClass, <span class="predefined-type">String</span> xml); <i class="conum" data-value="1"></i><b>(1)</b> <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="predefined-type">String</span> toXml(<span class="directive">final</span> <span class="predefined-type">Object</span> domainObject); <i class="conum" data-value="2"></i><b>(2)</b> <span class="directive">public</span> <span class="type">enum</span> IsisSchemas { <i class="conum" data-value="3"></i><b>(3)</b> INCLUDE, IGNORE } <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="predefined-type">Map</span>&lt;<span class="predefined-type">String</span>, <span class="predefined-type">String</span>&gt; toXsd(<span class="directive">final</span> <span class="predefined-type">Object</span> domainObject, <span class="directive">final</span> IsisSchemas isSchemas);} <i class="conum" data-value="4"></i><b>(4)</b> }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>unmarshalls the XML into an instance of the class.</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>marshalls the domain object into XML</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>whether to include or exclude the Isis schemas in the generated map of XSDs. Discussed further below.</td> </tr> <tr> <td><i class="conum" data-value="4"></i><b>4</b></td> <td>generates a map of each of the schemas referenced; the key is the schema namespace, the value is the XML of the schema itself.</td> </tr> </table> </div> <div class="paragraph"> <p>With respect to the <code>IsisSchemas</code> enum: a JAXB-annotated domain object will live in its own XSD namespace and may reference multiple other XSD schemas. In particular, many JAXB domain objects will reference the <a href="rgcms.html#_rgcms_schema">common Isis schemas</a> (for example the <code>OidDto</code> class that represents a reference to a persistent entity). The enum indicates whether these schemas should be included or excluded from the map.</p> </div> <div class="paragraph"> <p>Isis provides a default implementation of the service, <code>o.a.i.schema.services.jaxb.JaxbServiceDefault</code>.</p> </div> </div> <div class="sect3"> <h4 id="_usage_within_the_framework_2">2.15.2. Usage within the framework</h4> <div class="paragraph"> <p>This service is provided as a convenience for applications, but is also used internally by the framework to <a href="rgant.html#_rgant-XmlRootElement"><code>@XmlRootElement</code></a>-annotated <a href="ugbtb.html#_ugbtb_view-models">view models</a>. The functionality to download XML and XSD schemas is also exposed in the UI through mixins to <a href="rgcms.html#_rgcms_classes_roles_Dto"><code>Dto</code></a> interface.</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_service_7">2.15.3. Registering the Service</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' core implementation of <code>JaxbService</code> service is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_MementoService">2.16. <code>MementoService</code></h3> <div class="paragraph"> <p>The <code>MementoService</code> was originally introduced to simplify the implementation of <a href="ugbtb.html#_ugbtb_view-models">ViewModel</a>s which are required by the framework to return string representation of all of their backing state, moreover which is safe for use within a URL.</p> </div> <div class="paragraph"> <p>However, it can also be used to create a memento of arbitrary objects. Indeed, it is used internally by the core implementation of <a href="#_rgsvc_api_BackgroundService"><code>BackgroundService</code></a> to capture the state of action invocations so that they can be executed by a background process.</p> </div> <div class="sect3"> <h4 id="_api_implementation_11">2.16.1. API &amp; Implementation</h4> <div class="paragraph"> <p>The API defined by <code>MementoService</code> 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">interface</span> <span class="class">MementoService</span> { <span class="directive">public</span> <span class="directive">static</span> <span class="type">interface</span> <span class="class">Memento</span> { <span class="annotation">@Programmatic</span> <span class="directive">public</span> Memento set(<span class="predefined-type">String</span> name, <span class="predefined-type">Object</span> value); <span class="annotation">@Programmatic</span> <span class="directive">public</span> &lt;T&gt; T get(<span class="predefined-type">String</span> name, <span class="predefined-type">Class</span>&lt;T&gt; cls); <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="predefined-type">String</span> asString(); <span class="directive">public</span> <span class="predefined-type">Set</span>&lt;<span class="predefined-type">String</span>&gt; keySet(); } <span class="annotation">@Programmatic</span> <span class="directive">public</span> Memento create(); <span class="annotation">@Programmatic</span> <span class="directive">public</span> Memento parse(<span class="directive">final</span> <span class="predefined-type">String</span> str); <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="type">boolean</span> canSet(<span class="predefined-type">Object</span> input); }</code></pre> </div> </div> <div class="paragraph"> <p>The core framework provides a default implementation of this API, namely <code>o.a.i.core.runtime.services.memento.MementoServiceDefault</code>. The string returned (from <code>Memento#asString()</code>) is a base-64 URL encoded representation of the underlying format (an XML string).</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>In fact, the <code>MementoServiceDefault</code> implementation does provide a mechanism to disable the URL encoding, but this is not part of the <code>MementoService</code> public API. Note also that the encoding method is not pluggable.</p> </div> <div class="paragraph"> <p>However, you are of course free to write some other implementation of <code>MementoService</code>, perhaps based on <code>MementoServiceDefault</code> code if you wish.</p> </div> </td> </tr> </table> </div> <div class="paragraph"> <p>The types of objects that are supported by the <code>MementoService</code> are implementation-specific, but would typically include all the usual value types as well as Apache Isis' <code>Bookmark</code> class (to represent references to arbitrary entities). Nulls can also be set.</p> </div> <div class="paragraph"> <p>In the case of the default implementation provided by the core framework, the types supported are:</p> </div> <div class="ulist"> <ul> <li> <p><code>java.lang.String</code></p> </li> <li> <p><code>java.lang.Boolean</code>, <code>boolean</code></p> </li> <li> <p><code>java.lang.Byte</code>, <code>byte</code></p> </li> <li> <p><code>java.lang.Short</code>, <code>short</code></p> </li> <li> <p><code>java.lang.Integer</code>, <code>int</code></p> </li> <li> <p><code>java.lang.Long</code>, <code>long</code></p> </li> <li> <p><code>java.lang.Float</code>, <code>float</code></p> </li> <li> <p><code>java.lang.Double</code>, <code>double</code></p> </li> <li> <p><code>java.lang.Character</code>, <code>char</code></p> </li> <li> <p><code>java.math.BigDecimal</code></p> </li> <li> <p><code>java.math.BigInteger</code></p> </li> <li> <p><code>org.joda.time.LocalDate</code></p> </li> <li> <p><code>org.apache.isis.applib.services.bookmark.Bookmark</code></p> </li> </ul> </div> <div class="paragraph"> <p>If using another implementation, the <code>canSet(&#8230;&#8203;)</code> method can be used to check if the candidate object&#8217;s type is supported.</p> </div> </div> <div class="sect3"> <h4 id="_usage_6">2.16.2. Usage</h4> <div class="paragraph"> <p>As noted in the introduction, a common use case for this service is in the implementation of the <a href="rgcms.html#_rgcms_classes_super_AbstractViewModel"><code>ViewModel</code></a> interface.</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>Rather than implementing <code>ViewModel</code>, it&#8217;s usually easier to annotate your view models with <a href="rgant.html#_rgant-ViewModel"><code>@ViewModel</code></a> (or equivalently <a href="rgant.html#_rgant-DomainObject_nature"><code>@DomainObject#nature=EXTERNAL_ENTITY</code></a> or <a href="rgant.html#_rgant-DomainObject_nature"><code>@DomainObject#nature=INMEMORY_ENTITY</code></a>.</p> </div> </td> </tr> </table> </div> <div class="paragraph"> <p>For example, suppose you were implementing a view model that represents an external entity in a SOAP web service. To access this service the view model needs to store (say) the hostname, port number and an id to the object.</p> </div> <div class="paragraph"> <p>Using an injected <code>MementoService</code> the view model can roundtrip to and from this string, thus implementing the <code>ViewModel</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">class</span> <span class="class">ExternalEntity</span> <span class="directive">implements</span> ViewModel { <span class="directive">private</span> <span class="predefined-type">String</span> hostname; <span class="directive">private</span> <span class="type">int</span> port; <span class="directive">private</span> <span class="predefined-type">String</span> id; <span class="directive">public</span> <span class="predefined-type">String</span> viewModelMemento() { <i class="conum" data-value="1"></i><b>(1)</b> <span class="keyword">return</span> mementoService.create() .set(<span class="string"><span class="delimiter">&quot;</span><span class="content">hostname</span><span class="delimiter">&quot;</span></span>, hostname) .set(<span class="string"><span class="delimiter">&quot;</span><span class="content">port</span><span class="delimiter">&quot;</span></span>, port) .set(<span class="string"><span class="delimiter">&quot;</span><span class="content">id</span><span class="delimiter">&quot;</span></span>, id) .asString(); } <span class="directive">public</span> <span class="type">void</span> viewModelInit(<span class="predefined-type">String</span> mementoStr) { <i class="conum" data-value="2"></i><b>(2)</b> Memento memento = mementoService.parse(mementoStr); hostname = memento.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">hostname</span><span class="delimiter">&quot;</span></span>, <span class="predefined-type">String</span>.class); port = memento.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">port</span><span class="delimiter">&quot;</span></span>, <span class="type">int</span>.class); id = memento.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">id</span><span class="delimiter">&quot;</span></span>, <span class="predefined-type">String</span>.class); ... <span class="annotation">@Inject</span> MementoService mementoService; }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>part of the <code>ViewModel</code> API</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>part of the <code>ViewModel</code> API</td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_related_services_6">2.16.3. Related Services</h4> <div class="paragraph"> <p>The memento service is used by the <a href="#_rgsvc_api_CommandContext"><code>CommandContext</code></a> service and also <a href="#_rgsvc_spi_BackgroundCommandService"><code>BackgroundCommandService</code></a>. These both use a memento to capture a representation of an action invocation.</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_service_8">2.16.4. Registering the Service</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' core implementation of <code>MementoService</code> service is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_MetamodelService">2.17. <code>MetamodelService</code></h3> <div class="paragraph"> <p>The <code>MetaModelService</code> provides access (albeit currently extremely limited) to aspects of Apache Isis' internal metamodel.</p> </div> <div class="paragraph"> <p>Currently this is limited to looking up the object type (as specified in <a href="rgant.html#_rgant-DomainObject_objectType"><code>@DomainObject#objectType()</code></a> and equivalent mechanisms, and as used in <a href="#_rgsvc_api_BookmarkService"><code>Bookmark</code></a>s and elsewhere) from an object&#8217;s class, and vice versa. In the future we expect other aspects of the metamodel to also be formally surfaced through this API.</p> </div> <div class="sect3"> <h4 id="_api_implementation_12">2.17.1. API &amp; Implementation</h4> <div class="paragraph"> <p>The API defined by the service 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">interface</span> <span class="class">MetaModelService</span> { <span class="annotation">@Programmatic</span> <span class="predefined-type">Class</span>&lt;?&gt; fromObjectType(<span class="directive">final</span> <span class="predefined-type">String</span> objectType); <i class="conum" data-value="1"></i><b>(1)</b> <span class="annotation">@Programmatic</span> <span class="predefined-type">String</span> toObjectType(<span class="directive">final</span> <span class="predefined-type">Class</span>&lt;?&gt; domainType); <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>reverse lookup of a domain class' object type</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>lookup of a domain class' object type</td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_registering_the_service_9">2.17.2. Registering the Service</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' core implementation of <code>MetamodelService</code> service is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_QueryResultsCache">2.18. <code>QueryResultsCache</code></h3> <div class="paragraph"> <p>The purpose of the <code>QueryResultsCache</code> is to improve response times to the user, by providing a short-term (<a href="../../more-advanced-topics/how-to-09-020-How-to-write-a-typical-domain-service.html">request-scoped</a>) cache of the value of some (safe or idempotent) method call. This will typically be as the result of running a query, but could be any expensive operation.</p> </div> <div class="paragraph"> <p>Caching such values is useful for code that loops "naively" through a bunch of stuff, performing an expensive operation each time. If the data is such that the same expensive operation is made many times, then the query cache is a perfect fit.</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>This service was inspired by similar functionality that exists in relational databases, for example Sybase&#8217;s <a href="http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.dc20023_1251/html/optimizer/X43480.htm">subquery results cache</a> and Oracle&#8217;s <a href="http://www.dba-oracle.com/oracle11g/oracle_11g_result_cache_sql_hint.htm">result_cache</a> hint.</p> </div> </td> </tr> </table> </div> <div class="sect3"> <h4 id="_api_implementation_13">2.18.1. API &amp; Implementation</h4> <div class="paragraph"> <p>The API defined by <code>QueryResultsCache</code> is:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@RequestScoped</span> <span class="directive">public</span> <span class="type">class</span> <span class="class">QueryResultsCache</span> { <span class="directive">public</span> <span class="directive">static</span> <span class="type">class</span> <span class="class">Key</span> { <span class="directive">public</span> <span class="predefined-type">Key</span>(<span class="predefined-type">Class</span>&lt;?&gt; callingClass, <span class="predefined-type">String</span> methodName, <span class="predefined-type">Object</span>... keys) {...} <span class="directive">public</span> <span class="predefined-type">Class</span>&lt;?&gt; getCallingClass() { ... } <span class="directive">public</span> <span class="predefined-type">String</span> getMethodName() { ... } <span class="directive">public</span> <span class="predefined-type">Object</span><span class="type">[]</span> getKeys() { ... } } <span class="directive">public</span> <span class="directive">static</span> <span class="type">class</span> <span class="class">Value</span>&lt;T&gt; { <span class="directive">public</span> Value(T result) { ... } <span class="directive">private</span> T result; <span class="directive">public</span> T getResult() { <span class="keyword">return</span> result; } } <span class="annotation">@Programmatic</span> <span class="directive">public</span> &lt;T&gt; T execute( <span class="directive">final</span> <span class="predefined-type">Callable</span>&lt;T&gt; callable, <span class="directive">final</span> <span class="predefined-type">Class</span>&lt;?&gt; callingClass, <span class="directive">final</span> <span class="predefined-type">String</span> methodName, <span class="directive">final</span> <span class="predefined-type">Object</span>... keys) { ... } <span class="annotation">@Programmatic</span> <span class="directive">public</span> &lt;T&gt; T execute(<span class="directive">final</span> <span class="predefined-type">Callable</span>&lt;T&gt; callable, <span class="directive">final</span> <span class="predefined-type">Key</span> cacheKey) { ... } <span class="annotation">@Programmatic</span> <span class="directive">public</span> &lt;T&gt; Value&lt;T&gt; get( <span class="directive">final</span> <span class="predefined-type">Class</span>&lt;?&gt; callingClass, <span class="directive">final</span> <span class="predefined-type">String</span> methodName, <span class="directive">final</span> <span class="predefined-type">Object</span>... keys) { ... } <span class="annotation">@Programmatic</span> <span class="directive">public</span> &lt;T&gt; Value&lt;T&gt; get(<span class="directive">final</span> <span class="predefined-type">Key</span> cacheKey) { ... } <span class="annotation">@Programmatic</span> <span class="directive">public</span> &lt;T&gt; <span class="type">void</span> put(<span class="directive">final</span> <span class="predefined-type">Key</span> cacheKey, <span class="directive">final</span> T result) { ... } }</code></pre> </div> </div> <div class="paragraph"> <p>This class (<code>o.a.i.applib.services.queryresultscache.QueryResultsCache</code>) is also the implementation.</p> </div> </div> <div class="sect3"> <h4 id="_usage_7">2.18.2. Usage</h4> <div class="paragraph"> <p>Suppose that there&#8217;s a <code>TaxService</code> that calculates tax on <code>Taxable</code> items, with respect to some <code>TaxType</code>, and for a given <code>LocalDate</code>. To calculate tax it must run a database query and then perform some additional calculations.</p> </div> <div class="paragraph"> <p>Our original implementation is:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@DomainService</span> <span class="directive">public</span> <span class="type">class</span> <span class="class">TaxService</span> { <span class="directive">public</span> <span class="predefined-type">BigDecimal</span> calculateTax( <span class="directive">final</span> Taxable t, <span class="directive">final</span> TaxType tt, <span class="directive">final</span> LocalDate d) { <span class="comment">// query against DB using t, tt, d</span> <span class="comment">// further expensive calculations</span> } }</code></pre> </div> </div> <div class="paragraph"> <p>Suppose now that this service is called in a loop, for example iterating over a bunch of orders, where several of those orders are for the same taxable products, say. In this case the result of the calculation would always be the same for any given product.</p> </div> <div class="paragraph"> <p>We can therefore refactor the method to use the query cache as follows:</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">TaxService</span> { <span class="directive">public</span> <span class="predefined-type">BigDecimal</span> calculateTax( <span class="directive">final</span> Taxable t, <span class="directive">final</span> TaxType tt, <span class="directive">final</span> LocalDate d) { <span class="keyword">return</span> queryResultsCache.execute( <span class="keyword">new</span> <span class="predefined-type">Callable</span>&lt;<span class="predefined-type">BigDecimal</span>&gt;(){ <i class="conum" data-value="1"></i><b>(1)</b> <span class="directive">public</span> <span class="predefined-type">BigDecimal</span> call() <span class="directive">throws</span> <span class="exception">Exception</span> { <span class="comment">// query against DB using t, tt, d</span> <span class="comment">// further expensive calculations</span> } }, TaxService.class, <i class="conum" data-value="2"></i><b>(2)</b> <span class="string"><span class="delimiter">&quot;</span><span class="content">calculateTax</span><span class="delimiter">&quot;</span></span>, t, tt, d); } }</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>Callable</code> is the original code</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>the remaining parameters in essence uniquely identify the method call.</td> </tr> </table> </div> <div class="paragraph"> <p>This refactoring will be worthwhile provided that enough of the orders being processed reference the same taxable products. If however every order is for a different product, then no benefit will be gained from the refactoring.</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_service_10">2.18.3. Registering the Service</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' core implementation of <code>QueryResultsCache</code> service is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> <div class="sect3"> <h4 id="_related_services_7">2.18.4. Related Services</h4> <div class="paragraph"> <p>The <a href="#_rgsvc_api_Scratchpad"><code>Scratchpad</code></a> service is also intended for actions that are called many times, allowing arbitrary information to be shared between them. Those methods could be called from some outer loop in domain code, or by the framework itself if the action invoked has the <a href="rgant.html#_rgant-Action_invokeOn"><code>@Action#invokeOn()</code></a> annotation attribute set to <code>OBJECT_AND_COLLECTION</code> or <code>COLLECTION_ONLY</code>.</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_Scratchpad">2.19. <code>Scratchpad</code></h3> <div class="paragraph"> <p>The <code>Scratchpad</code> service is a <a href="../../more-advanced-topics/how-to-09-020-How-to-write-a-typical-domain-service.html">request-scoped</a> service to allow objects to exchange information even if they do not directly call each other.</p> </div> <div class="sect3"> <h4 id="_api_implementation_14">2.19.1. API &amp; Implementation</h4> <div class="paragraph"> <p>The API of <code>Scratchpad</code> service is:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@RequestScoped</span> <span class="directive">public</span> <span class="type">class</span> <span class="class">Scratchpad</span> { <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="predefined-type">Object</span> get(<span class="predefined-type">Object</span> key) { ... } <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="type">void</span> put(<span class="predefined-type">Object</span> key, <span class="predefined-type">Object</span> value) { ... } <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="type">void</span> clear() { ... } }</code></pre> </div> </div> <div class="paragraph"> <p>This class (<code>o.a.i.applib.services.scratchpad.Scratchpad</code>) is also the implementation. And, as you can see, the service is just a request-scoped wrapper around a <code>java.util.Map</code>.</p> </div> </div> <div class="sect3"> <h4 id="_usage_8">2.19.2. Usage</h4> <div class="paragraph"> <p>The most common use-case is for <a href="rgant.html#_rgant-Action_invokeOn">bulk</a> actions that act upon multiple objects in a list. The (same) <code>Scratchpad</code> service is injected into each of these objects, and so they can use pass information.</p> </div> <div class="paragraph"> <p>For example, the Isis addons example <a href="https://github.com/isisaddons/isis-app-todoapp/">todoapp</a> (not ASF) demonstrates how the <code>Scratchpad</code> service can be used to calculate the total cost of the selected `ToDoItem`s:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Action</span>( semantics=SemanticsOf.SAFE, invokeOn=InvokeOn.COLLECTION_ONLY ) <span class="directive">public</span> <span class="predefined-type">BigDecimal</span> totalCost() { <span class="predefined-type">BigDecimal</span> total = (<span class="predefined-type">BigDecimal</span>) scratchpad.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">runningTotal</span><span class="delimiter">&quot;</span></span>); <span class="keyword">if</span>(getCost() != <span class="predefined-constant">null</span>) { total = total != <span class="predefined-constant">null</span> ? total.add(getCost()) : getCost(); scratchpad.put(<span class="string"><span class="delimiter">&quot;</span><span class="content">runningTotal</span><span class="delimiter">&quot;</span></span>, total); } <span class="keyword">return</span> total.setScale(<span class="integer">2</span>); } <span class="annotation">@Inject</span> Scratchpad scratchpad;</code></pre> </div> </div> <div class="paragraph"> <p>A more complex example could use a <a href="ugbtb.html#_ugbtb_view-models">view model</a> to enable bulk updates to a set of objects. The view model&#8217;s job is to gather track of the items to be updated:</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">ToDoItemUpdateBulkUpdate</span> <span class="directive">extends</span> AbstractViewModel { <span class="directive">private</span> <span class="predefined-type">List</span>&lt;ToDoItem&gt; _items = ...; <span class="directive">public</span> ToDoItemBulkUpdate add(ToDoItem item) { _items.add(item); <span class="keyword">return</span> <span class="local-variable">this</span>; } ... <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>not shown - the implementation of <code>ViewModel</code> for converting the list of <code>_items</code> into a string.</td> </tr> </table> </div> <div class="paragraph"> <p>The bulk action in the objects simply adds the selected item to the view model:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Action</span>( invokeOn=InvokeOn.COLLECTIONS_ONLY semantics=SemanticsOf.SAFE ) <span class="directive">public</span> ToDoItemBulkUpdate bulkUpdate() { <span class="keyword">return</span> lookupBulkUpdateViewModel().add(<span class="local-variable">this</span>); } <span class="directive">private</span> ToDoItemBulkUpdate lookupBulkUpdateViewModel() { ToDoItemBulkUpdate bulkUpdate = (ToDoItemBulkUpdate) scratchpad.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">bulkUpdateViewModel</span><span class="delimiter">&quot;</span></span>); <i class="conum" data-value="1"></i><b>(1)</b> <span class="keyword">if</span>(bulkUpdate == <span class="predefined-constant">null</span>) { bulkUpdate = container.injectServicesInto(<span class="keyword">new</span> ToDoItemBulkUpdate()); scratchpad.put(<span class="string"><span class="delimiter">&quot;</span><span class="content">bulkUpdateViewModel</span><span class="delimiter">&quot;</span></span>, bulkUpdate); <i class="conum" data-value="2"></i><b>(2)</b> } <span class="keyword">return</span> bulkUpdate; } <span class="annotation">@Inject</span> Scratchpad scratchpad;</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>look for the <code>ToDoItemBulkUpdate</code> in the scratchpad&#8230;&#8203;</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>&#8230;&#8203; and add one if there isn&#8217;t one (ie for the first object returned).</td> </tr> </table> </div> <div class="paragraph"> <p>If using the <a href="ugvw.html">Wicket viewer</a>, the <code>ToDoItemBulkUpdate</code> view model returned from the last action invoked will be displayed. Thereafter this view model can be used to perform a bulk update of the "enlisted" items.</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_service_11">2.19.3. Registering the Service</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' core implementation of <code>Scratchpad</code> service is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> <div class="sect3"> <h4 id="_related_services_8">2.19.4. Related Services</h4> <div class="paragraph"> <p>The <a href="#_rgsvc_api_ActionInteractionContext"><code>ActionInteractionContext</code></a> service allows <a href="rgant.html#_rgant-Action_invokeOn">bulk actions</a> to co-ordinate with each other.</p> </div> <div class="paragraph"> <p>The <a href="#_rgsvc_api_QueryResultsCache"><code>QueryResultsCache</code></a> is useful for caching the results of expensive method calls.</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_SudoService">2.20. <code>SudoService</code></h3> <div class="paragraph"> <p>The <code>SudoService</code> allows the current user reported by the <code>DomainObjectContainer</code> to be temporarily changed to some other user. This is useful both for <a href="ugtst.html#_ugtst_integ-test-support">integration testing</a> (eg if testing a workflow system whereby objects are moved from one user to another) and while running <a href="ugtst.html#_ugtst_fixture-scripts">fixture scripts</a> (eg setting up objects that would normally require several users to have acted upon the objects).</p> </div> <div class="sect3"> <h4 id="_api_implementation_15">2.20.1. API &amp; Implementation</h4> <div class="paragraph"> <p>The API provided by the service 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">interface</span> <span class="class">SudoService</span> { <span class="annotation">@Programmatic</span> <span class="type">void</span> sudo(<span class="predefined-type">String</span> username, <span class="directive">final</span> <span class="predefined-type">Runnable</span> runnable); <span class="annotation">@Programmatic</span> &lt;T&gt; T sudo(<span class="predefined-type">String</span> username, <span class="directive">final</span> <span class="predefined-type">Callable</span>&lt;T&gt; callable); <span class="annotation">@Programmatic</span> <span class="type">void</span> sudo(<span class="predefined-type">String</span> username, <span class="predefined-type">List</span>&lt;<span class="predefined-type">String</span>&gt; roles, <span class="directive">final</span> <span class="predefined-type">Runnable</span> runnable); <span class="annotation">@Programmatic</span> &lt;T&gt; T sudo(<span class="predefined-type">String</span> username, <span class="predefined-type">List</span>&lt;<span class="predefined-type">String</span>&gt; roles, <span class="directive">final</span> <span class="predefined-type">Callable</span>&lt;T&gt; callable); }</code></pre> </div> </div> <div class="paragraph"> <p>which will run the provided block of code (a <code>Runnable</code> or a <code>Callable</code>) in a way such that calls to <code>DomainObjectContainer#getUser()</code> will return the specified user (and roles, if specified)</p> </div> <div class="paragraph"> <p>The core framework provides a default implementation of this service (<code>o.a.i.core.runtime.services.sudo.SudoServiceDefault</code>).</p> </div> </div> <div class="sect3"> <h4 id="_usage_9">2.20.2. Usage</h4> <div class="paragraph"> <p>A good example can be found in the (non-ASF) <a href="http://github.com/isisaddons/isis-app-todoapp">Isis addons' todoapp</a> which uses the <code>SudoService</code> in a fixture script to set up <code>ToDoItem</code> objects:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="directive">protected</span> <span class="type">void</span> execute(<span class="directive">final</span> ExecutionContext ec) { ... sudoService.sudo(getUsername(), <span class="keyword">new</span> <span class="predefined-type">Runnable</span>() { <span class="annotation">@Override</span> <span class="directive">public</span> <span class="type">void</span> run() { wrap(toDoItem).completed(); } }); ... }</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_registering_the_service_12">2.20.3. Registering the Service</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' core implementation of <code>SudoService</code> service is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_SwaggerService">2.21. <code>SwaggerService</code></h3> <div class="paragraph"> <p>The <code>SwaggerService</code> generates <a href="http://swagger.io/">Swagger</a> spec files to describe the public and/or private RESTful APIs exposed by the <a href="ugvro.html">RestfulObjects viewer</a>.</p> </div> <div class="paragraph"> <p>These spec files can then be used with the <a href="http://swagger.io/swagger-ui/">Swagger UI</a> page to explore the REST API, or used to generate client-side stubs using the <a href="http://swagger.io/swagger-codegen/">Swagger codegen</a> tool, eg for use in a custom REST client app.</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>Not all of the REST API exposed by the <a href="ugvro.html">Restful Objects viewer</a> is included in the Swagger spec files; the emphasis is those REST resources that are used to develop custom apps: domain objects, domain object collections and action invocations. When combined with Apache Isis' own <a href="ugvro.html#_ugvro_simplified-representations">simplified representations</a>, these are pretty much all that is needed for this use case.</p> </div> </td> </tr> </table> </div> <div class="sect3"> <h4 id="_rgsvc_api_SwaggerService_api-and-implementation">2.21.1. API &amp; Implementation</h4> <div class="paragraph"> <p>The API defined by <code>SwaggerService</code> 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">interface</span> <span class="class">SwaggerService</span> { <span class="type">enum</span> <span class="predefined-type">Visibility</span> { PUBLIC, <i class="conum" data-value="1"></i><b>(1)</b> PRIVATE, <i class="conum" data-value="2"></i><b>(2)</b> PRIVATE_WITH_PROTOTYPING; <i class="conum" data-value="3"></i><b>(3)</b> } <span class="type">enum</span> <span class="predefined-type">Format</span> { <i class="conum" data-value="4"></i><b>(4)</b> JSON, YAML } <span class="predefined-type">String</span> generateSwaggerSpec(<span class="directive">final</span> <span class="predefined-type">Visibility</span> visibility, <span class="directive">final</span> <span class="predefined-type">Format</span> format); }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>Generate a Swagger spec for use by third-party clients, ie public use. This specification is restricted only to <a href="ugbtb.html#_ugbtb_view-models">view model</a>s and to domain services with a <a href="rgant.html#_rgant_DomainService_nature">nature</a> of <code>VIEW_REST_ONLY</code>.</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>Generate a Swagger spec for use only by internally-managed clients, ie private internal use. This specification includes domain entities and all menu domain services (as well as any view models).</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>Generate a Swagger spec that is the same as private case (above), but also including any <a href="rgant.html#_rgant_Action_restrictTo">prototype</a> actions.</td> </tr> <tr> <td><i class="conum" data-value="4"></i><b>4</b></td> <td>Swagger specs can be written either in JSON or YAML format.</td> </tr> </table> </div> <div class="paragraph"> <p>Isis provides a default implementation of the service, <code>o.a.i.core.metamodel.services.swagger.SwaggerServiceDefault</code>.</p> </div> </div> <div class="sect3"> <h4 id="_usage_within_the_framework_3">2.21.2. Usage within the framework</h4> <div class="paragraph"> <p>This service is provided as a convenience for applications, it is not (currently) used by the framework itself.</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_service_13">2.21.3. Registering the Service</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' core implementation of <code>SwaggerService</code> service is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> </div> <div class="sect3"> <h4 id="_related_services_9">2.21.4. Related Services</h4> <div class="paragraph"> <p>A <code>SwaggerServiceMenu</code> domain service provides a prototype action that enables the swagger spec to be downloaded from the Wicket viewer&#8217;s UI.</p> </div> <div class="paragraph"> <p>Apache Isis' <a href="cgcon.html#_cgcon_isis-maven-plugin">Maven plugin</a> also provides a <a href="cgcon.html#_cgcon_isis-maven-plugin_swagger">swagger goal</a> which allows the spec file(s) to be generated at build time. this then allows client-side stubs can then be generated in turn as part of a build pipeline.</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_WrapperFactory">2.22. <code>WrapperFactory</code></h3> <div class="paragraph"> <p>The <code>WrapperFactory</code> provides the ability to enforce business rules for programmatic interactions between domain objects. If there is a (lack-of-) trust boundary between the caller and callee&#8201;&#8212;&#8201;eg if they reside in different modules&#8201;&#8212;&#8201;then the wrapper factory is a useful mechanism to ensure that any business constraints defined by te callee are honoured.</p> </div> <div class="paragraph"> <p>For example, if the calling object attempts to modify an unmodifiable property on the target object, then an exception will be thrown. Said another way: interactions are performed "as if" they are through the 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>For a discussion of the use of the <code>WrapperFactory</code> within integration tests (the primary or at least original use case for this service) can be found <a href="ugtst.html#_ugtst_integ-test-support_wrapper-factory">here</a></p> </div> </td> </tr> </table> </div> <div class="paragraph"> <p>This capability goes beyond enforcing the (imperative) constraints within the <code>hideXxx()</code>, <code>disableXxx()</code> and <code>validateXxx()</code> supporting methods; it also enforces (declarative) constraints such as those represented by annotations, eg <code>@MaxLength</code> or <code>@Regex</code>.</p> </div> <div class="paragraph"> <p>This capability is frequently used within <a href="ugtst.html#_ugtst_integ-test-support">integration tests</a>, but can also be used in production code. (There are analogies that can be drawn here with the way that JEE beans can interact through an EJB local interface).</p> </div> <div class="sect3"> <h4 id="_api_2">2.22.1. API</h4> <div class="paragraph"> <p>The API provided by the service 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">interface</span> <span class="class">WrapperFactory</span> { <span class="annotation">@Programmatic</span> &lt;T&gt; T wrap(T domainObject); <i class="conum" data-value="1"></i><b>(1)</b> <span class="annotation">@Programmatic</span> &lt;T&gt; T unwrap(T possibleWrappedDomainObject); <i class="conum" data-value="2"></i><b>(2)</b> <span class="annotation">@Programmatic</span> &lt;T&gt; <span class="type">boolean</span> isWrapper(T possibleWrappedDomainObject); <i class="conum" data-value="3"></i><b>(3)</b> <span class="directive">public</span> <span class="directive">static</span> <span class="type">enum</span> ExecutionMode { <i class="conum" data-value="4"></i><b>(4)</b> EXECUTE(<span class="predefined-constant">true</span>,<span class="predefined-constant">true</span>), SKIP_RULES(<span class="predefined-constant">false</span>, <span class="predefined-constant">true</span>), <i class="conum" data-value="5"></i><b>(5)</b> NO_EXECUTE(<span class="predefined-constant">true</span>, <span class="predefined-constant">false</span>); <i class="conum" data-value="6"></i><b>(6)</b> } <span class="annotation">@Programmatic</span> &lt;T&gt; T wrap(T domainObject, ExecutionMode mode); <i class="conum" data-value="7"></i><b>(7)</b> <span class="annotation">@Programmatic</span> &lt;T&gt; T wrapNoExecute(T domainObject); <i class="conum" data-value="8"></i><b>(8)</b> <span class="annotation">@Programmatic</span> &lt;T&gt; T wrapSkipRules(T domainObject); <i class="conum" data-value="9"></i><b>(9)</b> ... }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>wraps the underlying domain object. If it is already wrapped, returns the object back unchanged.</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>Obtains the underlying domain object, if wrapped. If the object is not wrapped, returns back unchanged.</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>whether the supplied object has been wrapped.</td> </tr> <tr> <td><i class="conum" data-value="4"></i><b>4</b></td> <td>enumerates how the wrapper interacts with the underlying domain object.</td> </tr> <tr> <td><i class="conum" data-value="5"></i><b>5</b></td> <td>validate all business rules and then execute.</td> </tr> <tr> <td><i class="conum" data-value="6"></i><b>6</b></td> <td>skip all business rules and then execute (including creating <a href="rgant.html#_rgant-Action_command">command</a>s and firing pre- and post-execute <a href="rgant.html#_rgant-Action_domainEvent">domain event</a>s).</td> </tr> <tr> <td><i class="conum" data-value="7"></i><b>7</b></td> <td>validate all business rules (including those from <a href="rgant.html#_rgant-Action_domainEvent">domain event</a>s) but do not execute.</td> </tr> <tr> <td><i class="conum" data-value="8"></i><b>8</b></td> <td>convenience method to invoke <code>wrap(&#8230;&#8203;)</code> with <code>ExecuteMode#NO_EXECUTE</code> (make this feature more discoverable)</td> </tr> <tr> <td><i class="conum" data-value="9"></i><b>9</b></td> <td>convenience method to invoke <code>wrap(&#8230;&#8203;)</code> with <code>ExecuteMode#SKIP_RULES</code> (make this feature more discoverable)</td> </tr> </table> </div> <div class="paragraph"> <p>The service works by returning a "wrapper" around a supplied domain object (a <a href="http://www.javassist.org">javassist</a> proxy), and it is this wrapper that ensures that the hide/disable/validate rules implies by the Apache Isis programming model are enforced. The wrapper can be interacted with as follows:</p> </div> <div class="ulist"> <ul> <li> <p>a <code>get&#8230;&#8203;()</code> method for properties or collections</p> </li> <li> <p>a <code>set&#8230;&#8203;()</code> method for properties</p> </li> <li> <p>an <code>addTo&#8230;&#8203;()</code> or <code>removeFrom&#8230;&#8203;()</code> method for collections</p> </li> <li> <p>any action</p> </li> </ul> </div> <div class="paragraph"> <p>Calling any of the above methods may result in a (subclass of) <code>InteractionException</code> if the object disallows it. For example, if a property is annotated with <code>@Hidden</code> then a <code>HiddenException</code> will be thrown. Similarly if an action has a <code>validateXxx()</code> method and the supplied arguments are invalid then an <code>InvalidException</code> will be thrown.</p> </div> <div class="paragraph"> <p>In addition, the following methods may also be called:</p> </div> <div class="ulist"> <ul> <li> <p>the <a href="rgcms.html#_rgcms_methods_reserved_title"><code>title()</code></a> and <code>toString()</code> methods</p> </li> <li> <p>any <a href="rgcms.html#_rgcms_methods_prefixes_default"><code>default&#8230;&#8203;()</code></a>, <a href="rgcms.html#_rgcms_methods_prefixes_choices"><code>choices&#8230;&#8203;()</code></a> or <a href="rgcms.html#_rgcms_methods_prefixes_autoComplete"><code>autoComplete&#8230;&#8203;()</code></a> methods</p> </li> </ul> </div> <div class="paragraph"> <p>An exception will be thrown if any other methods are thrown.</p> </div> </div> <div class="sect3"> <h4 id="_usage_10">2.22.2. Usage</h4> <div class="paragraph"> <p>The caller will typically obtain the target object (eg from some repository) and then use the injected <code>WrapperFactory</code> to wrap it before interacting with it.</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">CustomerAgent</span> { <span class="annotation">@Action</span> <span class="directive">public</span> <span class="type">void</span> refundOrder(<span class="directive">final</span> Order order) { <span class="directive">final</span> Order wrappedOrder = wrapperFactory.wrap(order); <span class="keyword">try</span> { wrappedOrder.refund(); } <span class="keyword">catch</span>(InteractionException ex) { <i class="conum" data-value="1"></i><b>(1)</b> container.raiseError(ex.getMessage()); <i class="conum" data-value="2"></i><b>(2)</b> <span class="keyword">return</span>; } } ... <span class="annotation">@Inject</span> WrapperFactory wrapperFactory; <span class="annotation">@Inject</span> DomainObjectContainer container; }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>if any constraints on the <code>Order&#8217;s `refund()</code> action would be violated, then &#8230;&#8203;</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>&#8230;&#8203; these will be trapped and raised to the user as a warning.</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>It ought to be possible to implement an <a href="#_rgsvc_spi_ExceptionRecognizer"><code>ExceptionRecognizer</code></a>s that would allow the above boilerplate to be removed. This recognizer service would recognize the <code>InteractionException</code> and convert to a suitable message.</p> </div> <div class="paragraph"> <p>At the time of writing Apache Isis does not provide an out-of-the-box implementation of such an <code>ExceptionRecognizer</code>; but it should be simple enough to write one…</p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_listener_api">2.22.3. Listener API</h4> <div class="paragraph"> <p>The <code>WrapperFactory</code> also provides a listener API to allow other services to listen in on interactions.</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">WrapperFactory</span> { ... <span class="annotation">@Programmatic</span> <span class="predefined-type">List</span>&lt;InteractionListener&gt; getListeners(); <i class="conum" data-value="1"></i><b>(1)</b> <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="type">boolean</span> addInteractionListener(InteractionListener listener); <i class="conum" data-value="2"></i><b>(2)</b> <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="type">boolean</span> removeInteractionListener(InteractionListener listener); <i class="conum" data-value="3"></i><b>(3)</b> <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="type">void</span> notifyListeners(InteractionEvent ev); <i class="conum" data-value="4"></i><b>(4)</b> }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>all <code>InteractionListener</code>s that have been registered.</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>registers an <code>InteractionListener</code>, to be notified of interactions on all wrappers. The listener will be notified of interactions even on wrappers created before the listener was installed. (From an implementation perspective this is because the wrappers delegate back to the container to fire the events).</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>remove an <code>InteractionListener</code>, to no longer be notified of interactions on wrappers.</td> </tr> <tr> <td><i class="conum" data-value="4"></i><b>4</b></td> <td>used by the framework itself</td> </tr> </table> </div> <div class="paragraph"> <p>The original intent of this API was to enable test transcripts to be captured (in a BDD-like fashion) from integration tests. No such feature has yet been implemented however. Also, the capabilities have by and large been superceded by Apache Isis' support for domain events. We may therefore deprecate this API in the future.</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_service_14">2.22.4. Registering the Service</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' core implementation of <code>WrapperFactory</code> service is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_api_XmlSnapshotService">2.23. <code>XmlSnapshotService</code></h3> <div class="paragraph"> <p>The <code>XmlSnapshotService</code> provides the capability to generate XML snapshots (and if required corresponding XSD schemas) based on graphs of domain objects.</p> </div> <div class="paragraph"> <p>Typical use cases include creating mementos for business-focused auditing, such that a report could be generated as to which end-user performed a business action (perhaps for legal reasons). For one system that we know of, a digest of this snapshot of data is signed with the public encryption key so as to enforce non-repudiation.</p> </div> <div class="paragraph"> <p>Another use case is to grab raw data such that it could be merged into a report template or communication.</p> </div> <div class="paragraph"> <p>The service offers a basic API to create a snapshot of a single object, and an more flexible API that allows the size of the graph to be customized.</p> </div> <div class="paragraph"> <p>The core framework provides an implementation of this service (<code>o.a.i.core.runtime.services.xmlsnapshot.XmlSnapshotServiceDefault</code>).</p> </div> <div class="sect3"> <h4 id="_standard_api">2.23.1. Standard API</h4> <div class="paragraph"> <p>The (basic) API of <code>XmlSnapshotService</code> is:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code>public interface XmlSnapshotService { public interface Snapshot { Document getXmlDocument(); Document getXsdDocument(); String getXmlDocumentAsString(); String getXsdDocumentAsString(); } @Programmatic public XmlSnapshotService.Snapshot snapshotFor(Object domainObject); ... }</code></pre> </div> </div> <div class="paragraph"> <p>The most straight-forward usage of this service is simply:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">XmlSnapshot snapshot = xmlsnapshotService.snapshotFor(customer); <span class="predefined-type">Element</span> customerAsXml = snapshot.getXmlElement();</code></pre> </div> </div> <div class="paragraph"> <p>This will return an XML (document) element that contains the names and values of each of the customer&#8217;s value properties, along with the titles of reference properties, and also the number of items in collections.</p> </div> <div class="paragraph"> <p>As well as obtaining the XML snapshot, it is also possible to obtain an XSD schema that the XML snapshot conforms to.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code>XmlSnapshot snapshot = ...; Element customerAsXml = snapshot.getXmlElement(); Element customerXsd = snapshot.getXsdElement();</code></pre> </div> </div> <div class="paragraph"> <p>This can be useful for some tools. For example, <a href="http://www.altova.com/stylevision.html">Altova Stylevision</a> can use the XML and XSD to transform into reports. Please note that this link does not imply endorsement (nor even a recommendation that this is a good design).</p> </div> </div> <div class="sect3"> <h4 id="_builder_api">2.23.2. Builder API</h4> <div class="paragraph"> <p>The contents of the snapshot can be adjusted by including "paths" to other references or collections. To do this, the builder is used. The API for this 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">interface</span> <span class="class">XmlSnapshotService</span> { ... public <span class="type">interface</span> <span class="class">Builder</span> { <span class="type">void</span> includePath(<span class="directive">final</span> <span class="predefined-type">String</span> path); <span class="type">void</span> includePathAndAnnotation(<span class="predefined-type">String</span> path, <span class="predefined-type">String</span> annotation); XmlSnapshotService.Snapshot build(); } <span class="annotation">@Programmatic</span> <span class="directive">public</span> XmlSnapshotService.Builder builderFor(<span class="predefined-type">Object</span> domainObject); }</code></pre> </div> </div> <div class="paragraph"> <p>We start by obtaining a builder:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">XmlSnapshot.Builder builder = xmlsnapshotService.builderFor(customer);</code></pre> </div> </div> <div class="paragraph"> <p>Suppose now that we want the snapshot to also include details of the customer&#8217;s address, where <code>address</code> in this case is a reference property to an instance of the <code>Address</code> class. We can "walk-the-graph" by including these references within the builder.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">builder.includePath(<span class="string"><span class="delimiter">&quot;</span><span class="content">address</span><span class="delimiter">&quot;</span></span>);</code></pre> </div> </div> <div class="paragraph"> <p>We could then go further and include details of every order in the customer&#8217;s <code>orders</code> collection, and details of every product of every order:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">builder.includePath(<span class="string"><span class="delimiter">&quot;</span><span class="content">orders/product</span><span class="delimiter">&quot;</span></span>);</code></pre> </div> </div> <div class="paragraph"> <p>When all paths are included, then the builder can build the snapshot:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">XmlSnapshot snapshot = builder.build(); <span class="predefined-type">Element</span> customerAsXml = snapshot.getXmlElement();</code></pre> </div> </div> <div class="paragraph"> <p>All of this can be strung together in a fluent API:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="predefined-type">Element</span> customerAsXml = xmlsnapshotService.builderFor(customer) .includePath(<span class="string"><span class="delimiter">&quot;</span><span class="content">address</span><span class="delimiter">&quot;</span></span>) .includePath(<span class="string"><span class="delimiter">&quot;</span><span class="content">orders/product</span><span class="delimiter">&quot;</span></span>) .build() .getXmlElement();</code></pre> </div> </div> <div class="paragraph"> <p>As you might imagine, the resultant XML document can get quite large very quickly with only a few "include"s.</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>If an XSD schema is beng generated (using <code>snapshot.getXsdElement()</code> then note that for the XSD to be correct, the object being snapshotted must have non-null values for the paths that are `include()&#8217;d. If this isn&#8217;t done then the XSD will not be correct reflect for another snapshotted object that does have non-null values.</p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_automatic_inclusions">2.23.3. Automatic inclusions</h4> <div class="paragraph"> <p>If the domain object being snapshotted implements the <code>SnapshottableWithInclusions</code> interace, then this moves the responsibility for determining what is included within the snapshot from the caller to the snapshottable object itself:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code>public interface SnapshottableWithInclusions extends Snapshottable { List&lt;String&gt; snapshotInclusions(); }</code></pre> </div> </div> <div class="paragraph"> <p>If necessary, both approaches can be combined.</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>As an alternative to using <code>include()</code>, you might consider building a view model domain object which can reference only the relevant information required for the snapshot. For example, if only the 5 most recent Orders for a Customer were required, a <code>CustomerAndRecentOrders</code> view model could hold a collection of just those 5 <code>Order</code>s. Typically such view models would implement <code>SnapshottableWithInclusions</code>.</p> </div> <div class="paragraph"> <p>One reason for doing this is to provide a stable API between the domain model and whatever it is that might be consuming the XML. With a view model you can refactor the domain entities but still preserve a view model such that the XML is the same.</p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_convenience_api">2.23.4. Convenience API</h4> <div class="paragraph"> <p>The <code>XmlSnapshotService</code> also provides some API for simply manipulating XML:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code>public interface XmlSnapshotService { ... @Programmatic public Document asDocument(String xmlStr); <i class="conum" data-value="1"></i><b>(1)</b> @Programmatic public &lt;T&gt; T getChildElementValue( <i class="conum" data-value="2"></i><b>(2)</b> Element el, String tagname, Class&lt;T&gt; expectedCls); @Programmatic public Element getChildElement( <i class="conum" data-value="3"></i><b>(3)</b> Element el, String tagname); @Programmatic public String getChildTextValue(Element el); <i class="conum" data-value="4"></i><b>(4)</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 a convenience method to convert xml string back into a W3C Document</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>is a convenience method to extract the value of an XML element, based on its type.</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>is a convenience method to walk XML document.</td> </tr> <tr> <td><i class="conum" data-value="4"></i><b>4</b></td> <td>is a convenience method to obtain value of child text node.</td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_registering_the_service_15">2.23.5. Registering the Service</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' core implementation of <code>XmlSnapshotService</code> service is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> <div class="sect3"> <h4 id="_related_services_10">2.23.6. Related Services</h4> <div class="paragraph"> <p>The <a href="#_rgsvc_api_BookmarkService"><code>BookmarkService</code></a> provides a mechanism for obtaining a string representations of a single domain object.</p> </div> <div class="paragraph"> <p>The <a href="#_rgsvc_api_MementoService"><code>MementoService</code></a> also provides a mechanism for generating string representations of domain objects.</p> </div> <div class="paragraph"> <p>The <a href="#_rgsvc_api_JaxbService"><code>JaxbService</code></a> is a simple wrapper around standard JAXB functionality for generating both XMLs and XSDs from JAXB-annotated classes. Note that there is built-in support for JAXB classes (ie annotated with <a href="rgant.html#_rgant-XmlRootElement"><code>@XmlRootElement</code></a>) to be used as view models.</p> </div> </div> </div> </div> </div> <div class="sect1"> <h2 id="_rgsvc_spi">3. Domain Services SPI</h2> <div class="sectionbody"> <div class="paragraph"> <p>Apache Isis includes an extensive number of domain services for your domain objects to use; these are listed in <a href="#_rgsvc_api">Reference: Domain Services (API)</a> chapter.</p> </div> <div class="paragraph"> <p>There are other domain services that constitute not an API but an SPI; if present they are used by Apache Isis itself rather than by your domain objects. A good example of this is the <a href="#_rgsvc_spi_AuditingService"><code>AuditingService</code></a> service; if an implementation is present then Apache Isis will call that service in order to record audit log entries. If no implementation is available then Apache Isis will carry on regardless.</p> </div> <div class="paragraph"> <p>Some domain services can be considered both API and SPI; a good example is the <a href="#_rgsvc_api_EmailService"><code>EmailService</code></a> that is of direct use for domain objects wishing to send out emails, but is also used by the framework to support the <a href="ugvw.html#_ugvw_features_user-registration">user registration</a> functionality supported by the <a href="ugvw.html">Wicket viewer</a>. The same is true of the <a href="#_rgsvc_api_EventBusService"><code>EventBusService</code></a>; this can be used by domain objects to broadcast arbitrary events, but is also used by the framework to automatically emit events for <a href="rgant.html#_rgant-Action_domainEvent"><code>@Action#domainEvent()</code></a> etc.</p> </div> <div class="paragraph"> <p>For these hybrid services we have categorized the service as an "API" service. This chapter therefore contains only the strictly SPI services.</p> </div> <div class="paragraph"> <p>The table below lists the SPIs that are defined in the Isis applib (<code>o.a.i.core:isis-core-applib</code> module).</p> </div> <table class="tableblock frame-all grid-all spread"> <caption class="title">Table 3. Applib SPI Services</caption> <colgroup> <col style="width: 25%;"> <col style="width: 50%;"> <col style="width: 12%;"> <col style="width: 12%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">SPI</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-left valign-top">Implementation</th> <th class="tableblock halign-left valign-top">Notes</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_spi_AuditingService"><code>o.a.i.applib.</code><br> <code>services.audit</code><br> <code>AuditingService3</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Create an audit record for every changed property of every changed object within a transaction.</p> </div></div></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>AuditingService</code><br> <code>o.ia.m.audit</code><br> <code>isis-module-audit</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">related services: <code>AuditingService-</code><br> <code>Contributions</code>, <code>AuditingService-</code><br> <code>Repository</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_spi_BackgroundCommandService"><code>o.a.i.applib.</code><br> <code>services.background</code><br> <code>BackgroundCommandService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Persisted a memento of an action invocation such that it can be executed asynchronously ("in the background") eg by a scheduler.</p> </div></div></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>BackgroundCommandServiceJdo</code><br> <code>o.ia.m.command</code><br> <code>isis-module-command</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">related services: <code>BackgroundCommandService-</code><br> <code>JdoContributions</code>, <code>BackgroundCommandService-</code><br> <code>JdoRepository</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_spi_ClassDiscoveryService"><code>o.a.i.applib.</code><br> <code>services.classdiscovery</code><br> <code>ClassDiscoveryService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Mechanism to locate (from the classpath) classes with a specific annotation (eg <a href="rgant.html#_rgant-DomainService"><code>@DomainService</code></a>)</p> </div> <div class="paragraph"> <p>Subtypes of a given type (eg <a href="rgcms.html#_rgcms_classes_super_FixtureScript"><code>FixtureScript</code></a>).</p> </div></div></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>ClassDiscoveryService-</code><br> <code>UsingReflections</code><br> <code>o.a.i.core</code><br> <code>isis-core-applib</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">requires <code>org.reflections:reflections</code> as Maven dependency</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_spi_CommandService"><code>o.a.i.applib.</code><br> <code>services.command.spi</code><br> <code>CommandService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Service to act as a factory and repository (create and save) of command instances, ie representations of an action invocation. Used for command/auditing and background services.</p> </div></div></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>CommandServiceJdo</code><br> <code>o.ia.m.command</code><br> <code>isis-module-command</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">related services:<br> <code>CommandService-</code> `JdoContributions`, `CommandService-` <code>JdoRepository</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_spi_EmailNotificationService"><code>o.a.i.applib.</code><br> <code>services.userreg</code><br> <code>EmailNotificationService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Notify a user during <a href="#_rgsvc_spi_UserRegistrationService">self-registration</a> of users.</p> </div></div></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>EmailNotificationService-</code><br> <code>Default</code><br> <code>o.a.i.core</code><br> <code>isis-core-runtime</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">depends on:<br> a configured <code>EmailService</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_spi_ErrorReportingService"><code>o.a.i.applib.</code><br> <code>services.error</code><br> <code>ErrorReportingService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Record details of an error occurring in the system (eg in an external incident recording system such as JIRA), and return a more friendly (jargon-free) message to display to the end user, with optional reference (eg <code>XXX-1234</code>).</p> </div></div></td> <td class="tableblock halign-left valign-top"><p class="tableblock">(none)</p></td> <td class="tableblock halign-left valign-top"></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_spi_EventSerializer"><code>o.a.i.applib.</code><br> <code>services.publish</code><br> <code>EventSerializer</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Creates a representation of either an action invocation or a changed object being published through the <a href="#_rgsvc_spi_PublishingService"><code>PublishingService</code></a>.</p> </div></div></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>RestfulObjects-</code><br> <code>SpecEventSerializer</code><br> <code>o.ia.m.publishing</code><br> <code>isis-module-publishing</code></p></td> <td class="tableblock halign-left valign-top"></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_spi_ExceptionRecognizer"><code>o.a.i.applib.</code><br> <code>services.exceprecog</code><br> <code>ExceptionRecognizer2</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Convert certain exceptions (eg foreign or unique key violation in the database) into a format that can be rendered to the end-user.</p> </div></div></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>ExceptionRecognizer-</code><br> <code>CompositeFor-</code><br> <code>JdoObjectStore</code><br> <code>o.a.i.core</code><br> <code>isis-core-applib</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Extensible using composite pattern if required</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_spi_LocaleProvider"><code>o.a.i.applib.</code><br> <code>services.i18n</code><br> <code>LocaleProvider</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Request-scoped service to return the locale of the current user, in support of i18n (ie so that the app&#8217;s UI, messages and exceptions can be translated to the required locale by the <a href="#_rgsvc_spi_TranslationService"><code>TranslationService</code></a>.</p> </div></div></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>LocaleProviderWicket</code><br> <code>o.a.i.viewer</code><br> <code>isis-viewer-wicket-impl</code></p></td> <td class="tableblock halign-left valign-top"></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_spi_FixtureScriptsSpecificationProvider"><code>o.a.i.applib.</code><br> <code>services.fixturespec</code><br> <code>FixtureScripts-</code><br> <code>SpecificationProvider</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Provides settings for <a href="#_rgsvc_api_FixtureScriptsDefault"><code>FixtureScriptsDefault</code></a> fallback domain service for executing fixture scripts.</p> </div></div></td> <td class="tableblock halign-left valign-top"></td> <td class="tableblock halign-left valign-top"></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_spi_PublishingService"><code>o.a.i.applib.</code><br> <code>services.publish</code><br> <code>PublishingService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Publish any action invocations and changed objects, typically for interchange with an external system in a different bounded context.</p> </div></div></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>PublishingService</code><br> <code>o.ia.m.publishing</code><br> <code>isis-module-publishing</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">related services: <code>PublishingService-</code> ``Contributions``, `PublishingService-` <code>Repository</code>. <br> depends on:<br> <code>EventSerializer</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_spi_RoutingService"><code>o.a.i.applib.</code><br> <code>services.routing</code><br> <code>RoutingService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Return an alternative object than that returned by an action.</p> </div></div></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>RoutingServiceDefault</code><br> <code>o.a.i.core</code><br> <code>isis-core-applib</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The default implementation will return the home page (per <a href="#_rgsvc_api_HomePageProviderService"><code>HomePageProviderService</code></a>) if a void or null is returned.<br> Used by the <a href="ugvw.html">Wicket viewer</a> only.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_spi_TranslationService"><code>o.a.i.applib.</code><br> <code>services.i18n</code><br> <code>TranslationService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Translate an app&#8217;s UI, messages and exceptions for the current user (as per the locale provided by <a href="#_rgsvc_spi_LocaleProvider"><code>LocalProvider</code></a>.</p> </div></div></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>TranslationServicePo</code><br> <code>o.a.i.core</code><br> <code>isis-core-runtime</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">related services: <code>TranslationServicePoMenu</code><br> depends on:<br> <code>TranslationsResolver</code>, <code>LocaleProvider</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_spi_TranslationsResolver"><code>o.a.i.applib.</code><br> <code>services.i18n</code><br> <code>TranslationsResolver</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Obtain translations for a particuar phrase and locale, in support of i18n (ie so that the app&#8217;s UI, messages and exceptions can be translated to the required locale by the <a href="#_rgsvc_spi_TranslationService"><code>TranslationService</code></a></p> </div></div></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>TranslationsResolverWicket</code><br> <code>o.a.i.viewer</code><br> <code>isis-viewer-wicket-impl</code></p></td> <td class="tableblock halign-left valign-top"></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_spi_UrlEncodingService"><code>o.a.i.applib.</code><br> <code>services.urlencoding</code><br> <code>UrlEncodingService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Converts strings into a form safe for use within a URL. Used to convert view models mementos into usable URL form.</p> </div></div></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>UrlEncodingService</code><br> <code>UsingBaseEncoding</code><br> <code>o.a.i.applib</code><br> <code>isis-core-applib</code></p></td> <td class="tableblock halign-left valign-top"></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_spi_UserProfileService"><code>o.a.i.applib.</code><br> <code>services.userprof</code><br> <code>UserProfileService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Obtain an alternative (usually enriched/customized) name for the current user, to render in the UI.</p> </div></div></td> <td class="tableblock halign-left valign-top"></td> <td class="tableblock halign-left valign-top"></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_spi_UserRegistrationService"><code>o.a.i.applib.</code><br> <code>services.userreg</code><br> <code>UserRegistrationService</code></a></p></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>Create a new user account with the configured security mechanism.</p> </div></div></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>SecurityModule-</code><br> <code>AppUserRegistrationService</code><br> <code>o.ia.m.security</code><br> <code>isis-module-security</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">depends (implicitly) on:<br> a configured <code>EmailService</code></p></td> </tr> </tbody> </table> <div class="paragraph"> <p>Key:</p> </div> <div class="ulist"> <ul> <li> <p><code>o.a.i</code> is an abbreviation for <code>org.apache.isis</code></p> </li> <li> <p><code>o.ia.m</code> is an abbreviation for <code>org.isisaddons.module</code></p> </li> </ul> </div> <div class="paragraph"> <p>Where an implementation is available (on the classpath) then it is always registered automatically (that is, they are all (with one exception) annotated with <a href="rgant.html#_rgant-DomainService"><code>@DomainService</code></a>. The one exception is <a href="#_rgsvc_spi_ExceptionRecognizer"><code>ExceptionRecognizer</code></a>, which must be registered explicitly in <code>isis.properties</code>; this makes the service extensible (for new exceptions to be recognized).</p> </div> <div class="paragraph"> <p>There are also some "internal" SPI services whose definitions depend on the internals of the framework (rather than the applib).</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>We do not guarantee that <a href="http://semver.org">semantic versioning</a> will be honoured for these APIs.</p> </div> </td> </tr> </table> </div> <table class="tableblock frame-all grid-all spread"> <caption class="title">Table 4. "Internal" SPI Services</caption> <colgroup> <col style="width: 30%;"> <col style="width: 30%;"> <col style="width: 20%;"> <col style="width: 20%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">SPI</th> <th class="tableblock halign-left valign-top">Maven Module<br> Impl&#8217;n (g: a:)</th> <th class="tableblock halign-left valign-top">Implementation</th> <th class="tableblock halign-left valign-top">Notes</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_spi_ContentMappingService"><code>o.a.i.v.ro.</code><br> <code>rendering.service.conneg.</code><br> <code>ContentMappingService</code></a></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">(Attempt to) map the returned data into the representation required by the client&#8217;s HTTP <code>Accept</code> header.</p></td> <td class="tableblock halign-left valign-top"></td> <td class="tableblock halign-left valign-top"><div><div class="paragraph"> <p>No default implementation.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_spi_ContentNegotiationService"><code>o.a.i.v.ro.</code><br> <code>rendering.service.conneg.</code><br> <code>ContentNegotiationService</code></a></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Encodes the algorithm that delegates to any registered <a href="#_rgsvc_spi_ContentMappingService"><code>ContentMappingService</code></a>s.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>ContentNegotiationService-</code><br> <code>XRoDomainType</code><br> <code>o.a.i.core</code><br> <code>isis-core-viewer-restfulobjects-rendering</code></p></td> <td class="tableblock halign-left valign-top"><div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_rgsvc_spi_RepresentationService"><code>o.a.i.v.ro.</code><br> <code>rendering.service.</code><br> <code>RepresentationService</code></a></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Generates the representations, delegating to any registered <a href="#_rgsvc_spi_ContentNegotiationService"><code>ContentNegotiationService</code></a>s.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>RepresentationService-</code><br> <code>ForRestfulObjects</code><br> <code>o.a.i.core</code><br> <code>isis-core-viewer-restfulobjects-rendering</code></p></td> <td class="tableblock halign-left valign-top"><div></div></td> </tr> </tbody> </table> <div class="paragraph"> <p>Key:</p> </div> <div class="ulist"> <ul> <li> <p><code>o.a.i.v.ro</code> is an abbreviation for <code>org.apache.isis.viewer.restfulobjects</code></p> </li> </ul> </div> <div class="sect2"> <h3 id="_rgsvc_spi_AuditingService">3.1. <code>AuditingService3</code></h3> <div class="paragraph"> <p>The <code>AuditingService3</code> auditing service provides a simple mechanism to capture changes to data. It is called for each property that has changed on any domain object, as a set of pre- and post-values.</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>In case you are wondering what happened to <code>AuditingService</code> and <code>AuditingService2</code>, these were earlier versions of the SPI that have since been deprecated and removed.</p> </div> </td> </tr> </table> </div> <div class="sect3"> <h4 id="_spi">3.1.1. SPI</h4> <div class="paragraph"> <p>The SPI for the service 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">interface</span> <span class="class">AuditingService3</span> { <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="type">void</span> audit( <span class="directive">final</span> <span class="predefined-type">UUID</span> transactionId, <span class="predefined-type">String</span> targetClassName, <span class="directive">final</span> Bookmark target, <span class="predefined-type">String</span> memberIdentifier, <span class="directive">final</span> <span class="predefined-type">String</span> propertyName, <span class="directive">final</span> <span class="predefined-type">String</span> preValue, <span class="directive">final</span> <span class="predefined-type">String</span> postValue, <span class="directive">final</span> <span class="predefined-type">String</span> user, <span class="directive">final</span> java.sql.Timestamp timestamp); }</code></pre> </div> </div> <div class="paragraph"> <p>The framework will call this for each and every domain object property that is modified within a transaction.</p> </div> </div> <div class="sect3"> <h4 id="_implementation">3.1.2. Implementation</h4> <div class="paragraph"> <p>The most full-featured available implementation is the (non-ASF) <a href="http://github.com/isisaddons/isis-module-audit">Isis addons' Audit module</a>. This creates an audit records for each changed property (ie every time that <code>AuditingService3#audit(&#8230;&#8203;)</code> is called. The implementation is <code>org.isisaddons.module.audit.dom.AuditingService</code>.</p> </div> <div class="paragraph"> <p>The module also provides:</p> </div> <div class="ulist"> <ul> <li> <p><code>AuditingServiceMenu</code> service which provides actions to search for <code>AuditEntry</code>s, underneath an 'Activity' menu on the secondary menu bar.</p> </li> <li> <p><code>AuditingServiceRepository</code> service to to search for persisted <code>AuditEntry``s. None of its actions are visible in the user interface (they are all `@Programmatic</code>).</p> </li> <li> <p><code>AuditingServiceContributions</code> which contrbutes collections to the <a href="rgcms.html#_rgcms_classes_mixins_HasTransactionId"> <code>HasTransactionId</code></a> interface. This will therefore display all audit entries that occurred in a given transaction, in other words whenever a command, a published event or another audit entry is displayed.</p> </li> </ul> </div> <div class="paragraph"> <p>If you just want to debug (writing to stderr), you can instead configure <code>o.a.i.applib.services.audit.AuditingService3$Stderr</code></p> </div> </div> <div class="sect3"> <h4 id="_usage_11">3.1.3. Usage</h4> <div class="paragraph"> <p>The typical way to indicate that an object should be audited is to annotate it with the <a href="rgant.html#_rgant-DomainObject_auditing"><code>@DomainObject#auditing()</code></a> annotation.</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_services_8">3.1.4. Registering the Services</h4> <div class="paragraph"> <p>The (non-ASF) <a href="http://github.com/isisaddons/isis-module-audit">Isis addons' audit</a> module provides an implementation of this service (<code>AuditingService</code>), and also provides a number of related domain services (<code>AuditingServiceMenu</code>, <code>AuditingServiceRepository</code> and <code>AuditingServiceContributions</code>).</p> </div> <div class="paragraph"> <p>Assuming that an <code>AppManifest</code> is being used to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then this can be activated by updating the <code>pom.xml</code> and updating the <code>AppManifest#getModules()</code> method.</p> </div> <div class="paragraph"> <p>If menu items or contributions are not required in the UI, these can be suppressed either using security or by implementing a <a href="ugbtb.html#_ugbtb_decoupling_vetoing-visibility">vetoing subscriber</a>.</p> </div> </div> <div class="sect3"> <h4 id="_related_services_11">3.1.5. Related Services</h4> <div class="paragraph"> <p>The auditing service works very well with the <a href="#<em>rgsvc_spi_CommandService"><code>CommandService</code></a>. The <code>CommandService</code> captures the _cause</em> of an interaction (an action was invoked, a property was edited), while the <code>AuditingService3</code> captures the <em>effect</em> of that interaction in terms of changed state.</p> </div> <div class="paragraph"> <p>You may also want to configure the <a href="#_rgsvc_spi_PublishingService"><code>PublishingService</code></a>.</p> </div> <div class="paragraph"> <p>All three of these services collaborate implicitly by way of the <a href="rgcms.html#_rgcms_classes_mixins_HasTransactionId"><code>HasTransactionId</code></a> interface.</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_spi_BackgroundCommandService">3.2. <code>BackgroundCommandService</code></h3> <div class="paragraph"> <p>The <code>BackgroundCommandService</code> (SPI) service supports the <a href="#_rgsvc_api_BackgroundService"><code>BackgroundService</code></a> (API) service, persisting action invocations as commands such that they can subsequently be invoked in the background.</p> </div> <div class="paragraph"> <p>The <code>BackgroundService</code> is responsible for capturing a memento representing the action invocation, and then hands off to the <a href="#_rgsvc_spi_BackgroundCommandService"><code>BackgroundCommandService</code></a> <code>BackgroundCommandService</code> to actually persist it.</p> </div> <div class="paragraph"> <p>The persisting of commands is only half the story; there needs to be a separate process to read the commands and execute them. The abstract <a href="#_rgsvc_api_BackgroundService_BackgroundCommandExecution"><code>BackgroundCommandExecution</code></a> provides a mechanism to execute such commands. This can be considered an API, albeit "internal" because the implementation relies on internals of the framework.</p> </div> <div class="sect3"> <h4 id="_spi_2">3.2.1. SPI</h4> <div class="paragraph"> <p>The SPI of the <code>BackgroundCommandService</code> 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">interface</span> <span class="class">BackgroundCommandService</span> { <span class="type">void</span> schedule( ActionInvocationMemento aim, <i class="conum" data-value="1"></i><b>(1)</b> Command parentCommand, <i class="conum" data-value="2"></i><b>(2)</b> <span class="predefined-type">String</span> targetClassName, <span class="predefined-type">String</span> targetActionName, <span class="predefined-type">String</span> targetArgs); }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>is a wrapper around a <a href="#_rgsvc_api_MementoService"><code>MementoService</code></a>'s <code>Memento</code>, capturing the details of the action invocation to be retained (eg persisted to a database) so that it can be executed at a later time</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>reference to the parent <code>Command</code> requesting the action be performed as a background command. This allows information such as the initiating user to be obtained.</td> </tr> </table> </div> <div class="paragraph"> <p>The API of <code>ActionInvocationMemento</code> in turn 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">ActionInvocationMemento</span> { <span class="directive">public</span> <span class="predefined-type">String</span> getActionId() { ... } <span class="directive">public</span> <span class="predefined-type">String</span> getTargetClassName() { ... } <span class="directive">public</span> <span class="predefined-type">String</span> getTargetActionName() { ... } <span class="directive">public</span> Bookmark getTarget() { ... } <span class="directive">public</span> <span class="type">int</span> getNumArgs() { ... } <span class="directive">public</span> <span class="predefined-type">Class</span>&lt;?&gt; getArgType(<span class="type">int</span> num) <span class="directive">throws</span> <span class="exception">ClassNotFoundException</span> { ... } <span class="directive">public</span> &lt;T&gt; T getArg(<span class="type">int</span> num, <span class="predefined-type">Class</span>&lt;T&gt; type) { ... } <span class="directive">public</span> <span class="predefined-type">String</span> asMementoString() { ... } <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>lets the <code>BackgroundCommandService</code> implementation convert the action invocation into a simple string.</td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="__internal_spi">3.2.2. "Internal" SPI</h4> <div class="paragraph"> <p>The <code>BackgroundCommandExecution</code> (in isis-core) is an abstract template class for <a href="ugbtb.html#_ugbtb_headless-access_AbstractIsisSessionTemplate">headless access</a>, that defines an abstract hook method to obtain background `Command`s to be executed:</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 { ... protected <span class="directive">abstract</span> <span class="predefined-type">List</span>&lt;? <span class="directive">extends</span> Command&gt; findBackgroundCommandsToExecute(); ... }</code></pre> </div> </div> <div class="paragraph"> <p>The developer is required to implement this hook method in a subclass.</p> </div> </div> <div class="sect3"> <h4 id="_implementation_2">3.2.3. Implementation</h4> <div class="paragraph"> <p>The (non-ASF) <a href="http://github.com/isisaddons/isis-module-command">Isis addons' command</a> module provides an implementation (<code>org.isisaddons.module.command.dom.BackgroundCommandServiceJdo</code>) that persists <code>Command</code>s using the JDO/DataNucleus object store. It further provides a number of supporting services:</p> </div> <div class="ulist"> <ul> <li> <p><code>org.isisaddons.module.command.dom.BackgroundCommandServiceJdoRepository</code> is a repository to search for persisted background <code>Command</code>s</p> </li> <li> <p><code>org.isisaddons.module.command.dom.BackgroundCommandServiceJdoContributions</code> contributes actions for searching for persisted child and sibling <code>Command</code>s.</p> </li> </ul> </div> <div class="paragraph"> <p>The module also provides a concrete subclass of <code>BackgroundCommandExecution</code> that knows how to query for persisted (background) `Command`s such that they can be executed by a scheduler.</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>Details of setting up the Quartz scheduler to actually execute these persisted commands can be found on the <a href="#_rgsvc_api_BackgroundService"><code>BackgroundService</code></a> page.</p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_usage_12">3.2.4. Usage</h4> <div class="paragraph"> <p>Background commands can be created either declaratively or imperatively.</p> </div> <div class="paragraph"> <p>The declarative approach involves annotating an action using <a href="rgant.html#_rgant-Action_command"><code>@Action#command()</code></a> with <code>@Action#commandExecuteIn=CommandExecuteIn.BACKGROUND</code>.</p> </div> <div class="paragraph"> <p>The imperative approach involves explicitly calling the <a href="#_rgsvc_api_BackgroundService"><code>BackgroundService</code></a> from within domain object&#8217;s action.</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_services_9">3.2.5. Registering the Services</h4> <div class="paragraph"> <p>The (non-ASF) <a href="http://github.com/isisaddons/isis-module-command">Isis addons' command</a> module provides an implementation of this service (<code>BackgroundCommandService</code>), and also provides a number of related domain services (<code>BackgroundCommandServiceJdo</code>, <code>BackgroundCommandJdoRepository</code> and <code>BackgroundCommandServiceJdoContributions</code>). This module also provides service implementations of the <a href="#_rgsvc_spi_CommandService"><code>CommandService</code></a>.</p> </div> <div class="paragraph"> <p>Assuming that an <code>AppManifest</code> is being used to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then this can be activated by updating the <code>pom.xml</code> and updating the <code>AppManifest#getModules()</code> method.</p> </div> <div class="paragraph"> <p>If contributions are not required in the UI, these can be suppressed either using security or by implementing a <a href="ugbtb.html#_ugbtb_decoupling_vetoing-visibility">vetoing subscriber</a>.</p> </div> </div> <div class="sect3"> <h4 id="_related_services_12">3.2.6. Related Services</h4> <div class="paragraph"> <p>As discussed above, this service supports the <a href="#_rgsvc_api_BackgroundService"><code>BackgroundService</code></a> , persisting `Command`s such that they can be executed in the background.</p> </div> <div class="paragraph"> <p>There is also a tie-up with the <a href="#_rgsvc_api_CommandContext"><code>CommandContext</code></a> and its supporting <a href="#_rgsvc_spi_CommandService"><code>CommandService</code></a> domain service. The <code>CommandContext</code> service is responsible for providing a parent <code>Command</code> with which the background <code>Command`s can then be associated as children, while the `CommandService</code> is responsible for persisting those parent <code>Command`s (analogous to the way in which the `BackgroundCommandService</code> persists the child background <code>Command`s). The `BackgroundCommandService</code> ensures that these background <code>Command`s are associated with the parent "foreground" `Command</code>.</p> </div> <div class="paragraph"> <p>What that means is that the implementations of <code>CommandService</code> and <code>BackgroundCommandService</code> go together, hence both implemented in the (non-ASF) <a href="http://github.com/isisaddons/isis-module-command">Isis addons' command</a> module.).</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_spi_ClassDiscoveryService">3.3. <code>ClassDiscoveryService</code></h3> <div class="paragraph"> <p>The <code>ClassDiscovery</code> service is used to automatically discover subclasses of any given type on the classpath. The primary use case is to support "convention-over-configuration" designs that work with a minimum of configuration.</p> </div> <div class="paragraph"> <p>This service is used by the <a href="rgcms.html#_rgcms_classes_super_FixtureScripts"><code>FixtureScripts</code></a> service to automatically locate any <a href="rgcms.html#_rgcms_classes_super_FixtureScript"><code>FixtureScript</code></a> implementations.</p> </div> <div class="sect3"> <h4 id="_spi_3">3.3.1. SPI</h4> <div class="paragraph"> <p>The SPI defined by the service 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">interface</span> <span class="class">ClassDiscoveryService2</span> { <span class="annotation">@Programmatic</span> &lt;T&gt; <span class="predefined-type">Set</span>&lt;<span class="predefined-type">Class</span>&lt;? <span class="directive">extends</span> T&gt;&gt; findSubTypesOfClasses(<span class="predefined-type">Class</span>&lt;T&gt; type, <span class="predefined-type">String</span> packagePrefix); <span class="annotation">@Deprecated</span> <span class="annotation">@Programmatic</span> &lt;T&gt; <span class="predefined-type">Set</span>&lt;<span class="predefined-type">Class</span>&lt;? <span class="directive">extends</span> T&gt;&gt; findSubTypesOfClasses(<span class="predefined-type">Class</span>&lt;T&gt; type); <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>no longer used</td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_implementation_3">3.3.2. Implementation</h4> <div class="paragraph"> <p>Isis provides an implementation of this service, namely <code>o.a.i.applib.services.classdiscovery.ClassDiscoveryServiceUsingReflections</code>.</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>This implementation is also used to discover domain services annotated with <a href="rgant.html#_rgant-DomainService"><code>@DomainService</code></a>. Currently this logic uses the implementation directly, so is not pluggable. However, the entire <code>ServicesInstaller</code></p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_usage_13">3.3.3. Usage</h4> <div class="paragraph"> <p>The usage will vary depending upon the conventions of the design. As of 1.9.0, the usage of the service has been centralized such that the packages to be scanned are located from the <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping"><code>AppManifest</code></a>'s <code>#getModules()</code> method.</p> </div> <div class="paragraph"> <p>For example, the <a href="ugfun.html#_ugfun_getting-started_simpleapp-archetype">SimpleApp archetype</a>'s app manifest includes:</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">DomainAppAppManifest</span> <span class="directive">implements</span> AppManifest { <span class="annotation">@Override</span> <span class="directive">public</span> <span class="predefined-type">List</span>&lt;<span class="predefined-type">Class</span>&lt;?&gt;&gt; getModules() { <span class="keyword">return</span> <span class="predefined-type">Arrays</span>.asList( DomainAppDomainModule.class, <span class="comment">// domain (entities and repositories)</span> DomainAppFixtureModule.class, <span class="comment">// fixtures</span> DomainAppAppModule.class <span class="comment">// home page service etc</span> ); } ... }</code></pre> </div> </div> <div class="paragraph"> <p>where the three module classes in effect define three different package prefixes to search under (for domain services, fixture scripts and persistent entities).</p> </div> <div class="paragraph"> <p>Other usages of the <code>ClassDiscoveryService</code> are likely to work in a similar way, requiring some sort of scope to be specified.</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_services_10">3.3.4. Registering the Services</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' core implementation of <code>ClassDiscoveryService2</code> service is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_spi_CommandService">3.4. <code>CommandService</code></h3> <div class="paragraph"> <p>The <code>CommandService</code> service supports the <a href="#_rgsvc_api_CommandContext"><code>CommandContext</code></a> service such that <code>Command</code> objects (that reify the invocation of an action on a domain object into an object) can be persisted.</p> </div> <div class="paragraph"> <p>Persistent `Command`s support several use cases:</p> </div> <div class="ulist"> <ul> <li> <p>they enable profiling of the running application (which actions are invoked then most often, what is their response time)</p> </li> <li> <p>they act as a parent to any background commands that might be invoked through the <a href="#_rgsvc_api_BackgroundService"><code>BackgroundService</code></a></p> </li> <li> <p>if <a href="#_rgsvc_spi_AuditingService">auditing</a> is configured, they provide better audit information, since the <code>Command</code> (the 'cause' of an action) can be correlated to the audit records (the "effect" of the action) by way of the <a href="rgcms.html#_rgcms_classes_mixins_HasTransactionId"><code>transactionId</code></a></p> </li> <li> <p>if <a href="#_rgsvc_spi_PublishingService">publishing</a> is configured, they provide better traceability as the <code>Command</code> is also correlated with any published events, again through the unique <a href="rgcms.html#_rgcms_classes_mixins_HasTransactionId"><code>transactionId</code></a></p> </li> <li> <p>the associated <a href="rgant.html#_rgant-Action_command"><code>@Action#command()</code></a> annotation attribute also allows action invocations to be performed in the background. In this case the act of invoking the action on an object instead returns the <code>Command</code> to the user.</p> </li> </ul> </div> <div class="paragraph"> <p>The screencast below provides a run-through of the command (profiling) service, auditing service, publishing service. It also shows how commands can be run in the background either explicitly by scheduling through the background service or implicitly by way of a framework annotation.</p> </div> <div class="videoblock"> <div class="content"> <iframe width="560px" height="315px" src="//www.youtube.com/embed/tqXUZkPB3EI?rel=0" frameborder="0" allowfullscreen></iframe> </div> </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 screencast shows an earlier version of the <a href="ugvw.html">Wicket viewer</a> UI (specifically, pre 1.8.0).</p> </div> </td> </tr> </table> </div> <div class="sect3"> <h4 id="_spi_4">3.4.1. SPI</h4> <div class="paragraph"> <p>The <code>CommandService</code> service defines the following very simple 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">CommandService</span> { <span class="annotation">@Programmatic</span> Command create(); <span class="annotation">@Programmatic</span> <span class="type">void</span> startTransaction(Command command, <span class="directive">final</span> <span class="predefined-type">UUID</span> transactionId); <span class="annotation">@Programmatic</span> <span class="type">void</span> complete(Command command); <span class="annotation">@Programmatic</span> <span class="type">boolean</span> persistIfPossible(Command command); }</code></pre> </div> </div> <div class="paragraph"> <p>where <code>Command</code> is defined as defined by the <a href="#_rgsvc_api_CommandContext"><code>CommandContext</code></a> service.</p> </div> </div> <div class="sect3"> <h4 id="_implementation_4">3.4.2. Implementation</h4> <div class="paragraph"> <p>The (non-ASF) <a href="http://github.com/isisaddons/isis-module-command">Isis addons' command</a> module provides an implementation (<code>org.isisaddons.module.command.dom.CommandServiceJdo</code>) that persists <code>Command</code>s using the JDO/DataNucleus object store. It further provides a number of supporting services:</p> </div> <div class="ulist"> <ul> <li> <p><code>org.isisaddons.module.command.dom.CommandServiceJdoRepository</code> is a repository to search for persisted <code>Command</code>s</p> </li> <li> <p><code>org.isisaddons.module.command.dom.CommandServiceJdoContributions</code> contributes actions for searching for persisted child and sibling <code>Command</code>s.</p> </li> </ul> </div> </div> <div class="sect3"> <h4 id="_usage_14">3.4.3. Usage</h4> <div class="paragraph"> <p>The typical way to indicate that an action should be reified into a <code>Command</code> is by annotating the action using <a href="rgant.html#_rgant-Action_command"><code>@Action#command()</code></a>.</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_services_11">3.4.4. Registering the Services</h4> <div class="paragraph"> <p>The (non-ASF) <a href="http://github.com/isisaddons/isis-module-command">Isis addons' command</a> module provides an implementation of this service (<code>CommandService</code>), and also provides a number of related domain services (<code>CommandJdoRepository</code> and <code>CommandServiceJdoContributions</code>). This module also provides service implementations of the <a href="#_rgsvc_spi_CommandService"><code>BackgroundCommandService</code></a>.</p> </div> <div class="paragraph"> <p>Assuming that an <code>AppManifest</code> is being used to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then this can be activated by updating the <code>pom.xml</code> and updating the <code>AppManifest#getModules()</code> method.</p> </div> <div class="paragraph"> <p>If contributions are not required in the UI, these can be suppressed either using security or by implementing a <a href="ugbtb.html#_ugbtb_decoupling_vetoing-visibility">vetoing subscriber</a>.</p> </div> </div> <div class="sect3"> <h4 id="_related_services_13">3.4.5. Related Services</h4> <div class="paragraph"> <p>As discussed above, this service supports the <a href="#_rgsvc_api_CommandContext"><code>CommandContext</code></a>, providing the ability for <code>Command</code> objects to be persisted. This is closely related to the <a href="#_rgsvc_spi_BackgroundCommandService"><code>BackgroundCommandService</code></a>that allows the <a href="#_rgsvc_api_BackgroundService"><code>BackgroundService</code></a> to schedule commands for background/asynchronous execution.</p> </div> <div class="paragraph"> <p>The implementations of <code>CommandService</code> and <code>BackgroundCommandService</code> are intended to go together, so that persistent parent `Command`s can be associated with their child background `Command`s.</p> </div> <div class="paragraph"> <p>The services provided by this module combines very well with the <a href="#<em>rgsvc_spi_AuditingService"><code>AuditingService</code></a>. The <code>CommandService</code> captures the _cause</em> of an interaction (an action was invoked, a property was edited), while the <code>AuditingService3</code> captures the <em>effect</em> of that interaction in terms of changed state.</p> </div> <div class="paragraph"> <p>You may also want to configure the <a href="#_rgsvc_spi_PublishingService"><code>PublishingService</code></a>.</p> </div> <div class="paragraph"> <p>All three of these services collaborate implicitly by way of the <a href="rgcms.html#_rgcms_classes_mixins_HasTransactionId"><code>HasTransactionId</code></a> interface.</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_spi_ContentMappingService">3.5. <code>ContentMappingService</code></h3> <div class="paragraph"> <p>The <code>ContentMappingService</code> supports the (default implementation of the) <a href="#_rgsvc_spi_ContentNegotiationService"><code>ContentNegotiationService</code></a> allowing the <a href="ugvro.html">RestfulObjects viewer</a> to allow domain objects to be transformed into some other format as specified by the HTTP <code>Accept</code> header.</p> </div> <div class="paragraph"> <p>See <a href="#_rgsvc_spi_ContentNegotiationService"><code>ContentNegotiationService</code></a> for further discussion.</p> </div> <div class="sect3"> <h4 id="_spi_5">3.5.1. SPI</h4> <div class="paragraph"> <p>The SPI defined by this service 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">interface</span> <span class="class">ContentMappingService</span> { <span class="annotation">@Programmatic</span> <span class="predefined-type">Object</span> map(<span class="predefined-type">Object</span> object, <i class="conum" data-value="1"></i><b>(1)</b> <span class="predefined-type">List</span>&lt;MediaType&gt; acceptableMediaTypes, <i class="conum" data-value="2"></i><b>(2)</b> RepresentationType representationType); <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>typically the input is a domain object (whose structure might change over time), and the output is a DTO (whose structure is guaranteed to be preserved over time)</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>as per the caller&#8217;s HTTP <code>Accept</code> header</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>the representation type required (as per the <a href="http://restfulobjects.org">Restful Objects spec</a>).</td> </tr> </table> </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>This is an "internal" SPI, meaning that it uses types that are not part of the Isis applib. We do not guarantee that <a href="http://semver.org">semantic versioning</a> will be honoured for these APIs.</p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_implementations_2">3.5.2. Implementations</h4> <div class="paragraph"> <p>No default implementations are provided by Apache Isis framework itself. However, the (non-ASF) <a href="http://github.com/isisaddons/isis-app-todoapp">Isis addons' todoapp</a> includes a sample implementation to convert its <code>ToDoItem</code> entity into (two different versions of) a <code>ToDoItemDto</code> (JAXB annotated). This uses the <a href="http://orika-mapper.github.io/orika-docs/intro.html">Orika</a> mapping library.</p> </div> <div class="paragraph"> <p>The source code is:</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">CustomContentMappingService</span> <span class="directive">implements</span> ContentMappingService { <span class="directive">private</span> MapperFactory mapperFactory; <i class="conum" data-value="1"></i><b>(1)</b> <span class="annotation">@Programmatic</span> <span class="annotation">@PostConstruct</span> <span class="directive">public</span> <span class="type">void</span> init() { mapperFactory = <span class="keyword">new</span> DefaultMapperFactory.Builder().build(); mapperFactory.registerClassMap( mapperFactory.classMap(ToDoItem.class, ToDoItemDto.class) <i class="conum" data-value="2"></i><b>(2)</b> .byDefault() <i class="conum" data-value="3"></i><b>(3)</b> .toClassMap()); mapperFactory.registerClassMap( mapperFactory.classMap(Bookmark.class, OidDto.class) <i class="conum" data-value="4"></i><b>(4)</b> .field(<span class="string"><span class="delimiter">&quot;</span><span class="content">identifier</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">objectIdentifier</span><span class="delimiter">&quot;</span></span>) <i class="conum" data-value="5"></i><b>(5)</b> .byDefault() <span class="comment">// all other fields are compatible </span><i class="conum" data-value="6"></i><b>(6)</b> .toClassMap()); } <span class="annotation">@Programmatic</span> <span class="annotation">@Override</span> <i class="conum" data-value="7"></i><b>(7)</b> <span class="directive">public</span> <span class="predefined-type">Object</span> map( <span class="directive">final</span> <span class="predefined-type">Object</span> object, <span class="directive">final</span> <span class="predefined-type">List</span>&lt;MediaType&gt; acceptableMediaTypes, <span class="directive">final</span> RepresentationType representationType) { <span class="keyword">if</span>(object <span class="keyword">instanceof</span> ToDoItem) { <span class="directive">final</span> Bookmark bookmark = bookmarkService.bookmarkFor(object); <span class="directive">final</span> ToDoItemDto dto = mapperFactory.getMapperFacade().map(object, ToDoItemDto.class); <i class="conum" data-value="8"></i><b>(8)</b> <span class="directive">final</span> OidDto oidDto = mapperFactory.getMapperFacade().map(bookmark, OidDto.class); <i class="conum" data-value="9"></i><b>(9)</b> dto.setOid(oidDto); <i class="conum" data-value="10"></i><b>(10)</b> <span class="keyword">return</span> dto; } <span class="keyword">return</span> <span class="predefined-constant">null</span>; } <span class="annotation">@javax</span>.inject.Inject <span class="directive">private</span> BookmarkService bookmarkService; }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>registry of known mappings</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>how to map <code>ToDoItem</code> to <code>ToDoItemDto</code> &#8230;&#8203;</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>&#8230;&#8203; all properties have same name in both source and destination types</td> </tr> <tr> <td><i class="conum" data-value="4"></i><b>4</b></td> <td>how to map <code>Bookmark</code> to <code>OidDto</code> &#8230;&#8203;</td> </tr> <tr> <td><i class="conum" data-value="5"></i><b>5</b></td> <td>&#8230;&#8203; this property has a different name</td> </tr> <tr> <td><i class="conum" data-value="6"></i><b>6</b></td> <td>&#8230;&#8203; all other properties have same name</td> </tr> <tr> <td><i class="conum" data-value="7"></i><b>7</b></td> <td>the main API to implement. This implementation ignores the <code>acceptableMediaTypes</code> and <code>representationType</code></td> </tr> <tr> <td><i class="conum" data-value="8"></i><b>8</b></td> <td>map the domain object to the DTO</td> </tr> <tr> <td><i class="conum" data-value="9"></i><b>9</b></td> <td>and map its oid (by way of the <a href="#_rgsvc_api_BookmarkService"><code>BookmarkService</code></a></td> </tr> <tr> <td><i class="conum" data-value="10"></i><b>10</b></td> <td>and perform some additional manual wiring (because domain objects don&#8217;t <em>know</em> their Oids).</td> </tr> </table> </div> <div class="paragraph"> <p>You&#8217;ll notice that the implementation doesn&#8217;t actually use the <code>acceptableMediaTypes</code> and <code>representationType</code> parameters. That&#8217;s because the calling <code>ContentNegotiationServiceXRoDomainType</code> will double check that the returned object is of the correct type (as defined by the <code>x-ro-domain-type</code> parameter of the HTTP <code>Accept</code> header). Since this (example) todoapp only offers a single mapping, there&#8217;s therefore no need to for the mapping service to check further.</p> </div> </div> <div class="sect3"> <h4 id="_related_services_14">3.5.3. Related Services</h4> <div class="paragraph"> <p>This service is a companion to the default implementation of the <a href="#_rgsvc_spi_ContentNegotiationService"><code>ContentNegotiationService</code></a>.</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_spi_ContentNegotiationService">3.6. <code>ContentNegotiationService</code></h3> <div class="paragraph"> <p>The <code>ContentNegotiationService</code> is a plug-in point for the <a href="ugvro.html">RestfulObjects viewer</a> so that it can generate representations according to HTTP <code>Accept</code> header of the request. This idea is discussed in section 34.1 of the <a href="http://restfulobjects.org">Restful Objects spec</a> v1.0.</p> </div> <div class="paragraph"> <p>The principal motivation is to allow more flexible representations to be generated for REST clients that (perhaps through their use of a certain Javascript library, say) expect, or at least works best with, a certain style of representation.</p> </div> <div class="paragraph"> <p>Another use case is to support "third party" REST clients over which you have no control. In this scenario you <em>must not</em> naively expose entities through the RO viewer, because over time those entities will inevitably evolve and change their structure. If the entities were exposed directly then those REST clients will break.</p> </div> <div class="paragraph"> <p>Instead you need to create some sort of stable facade over your domain entities, one which you will preserve even if the domain entities change. There are three ways in which you can do this:</p> </div> <div class="ulist"> <ul> <li> <p>first is to solve the problem at the domain layer by defining a regular Apache Isis <a href="ugbtb.html#_ugbtb_view-models">view model</a>. This is then surfaced over the RO viewer. <br></p> <div class="paragraph"> <p>If the underlying entities change, then care must be taken to ensure that structure of the view model nevertheless is unchanged.</p> </div> </li> <li> <p>a second option is to solve the problem at the persistence layer, but defining a (SQL) view in the database and then <a href="ugbtb.html#_ugbtb_other-techniques_mapping-rdbms-views">mapping this</a> to a (read-only) entity. Again this is surfaced by the RO viewer. <br></p> <div class="paragraph"> <p>If the underlying tables change (as the result of a change in their corresponding domain entities) then once more the view must be refactored so that it still presents the same structure.</p> </div> </li> <li> <p>our third option is to solve the problem at the presentation layer, using the <code>ContentNegotiationService</code> described in this section.<br></p> <div class="paragraph"> <p>The <code>ContentNegotiationService</code> is responsible for inspecting the HTTP <code>Accept</code> header, and use this to select the correct representation to render. <br></p> </div> <div class="paragraph"> <p>The Apache Isis framework provides a default implementation of <code>ContentNegotiationService</code> which inspects the "x-ro-domaintype" component of the HTTP <code>Accept</code> header. If present, this implementation will delegate to the companion <a href="#_rgsvc_spi_ContentMappingService"><code>ContentMappingService</code></a> service, if configured. <br></p> </div> <div class="paragraph"> <p>A typical implementation of <code>ContentMappingService</code> will convert the domain object into some sort of DTO (data transfer object) as specified by the "x-ro-domaintype". If this DTO is annotated with JAXB or Jackson mappings, then the RO viewer (courtesy of the underlying <a href="http://resteasy.jboss.org/">RestEasy</a> framework) can serialize these directly <br></p> </div> <div class="paragraph"> <p>What all that means is that, if the underlying entities change, we are required to update the mappings in the <code>ContentMappingService</code> to map to the same DTOs.</p> </div> </li> </ul> </div> <div class="paragraph"> <p>This diagram illustrates the three options available:</p> </div> <div class="imageblock"> <div class="content"> <a class="image" href="images/reference-services-spi/ContentNegotiationService/facade-choices.png"><img src="images/reference-services-spi/ContentNegotiationService/facade-choices.png" alt="facade choices" width="700px"></a> </div> </div> <div class="sect3"> <h4 id="_spi_6">3.6.1. SPI</h4> <div class="paragraph"> <p>The SPI defined by this service 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">interface</span> <span class="class">ContentNegotiationService</span> { <span class="annotation">@Programmatic</span> Response.ResponseBuilder buildResponse( <i class="conum" data-value="1"></i><b>(1)</b> RepresentationService.Context2 renderContext2, ObjectAdapter objectAdapter); <span class="annotation">@Programmatic</span> Response.ResponseBuilder buildResponse( <i class="conum" data-value="2"></i><b>(2)</b> RepresentationService.Context2 renderContext2, ObjectAndProperty objectAndProperty); <span class="annotation">@Programmatic</span> Response.ResponseBuilder buildResponse( <i class="conum" data-value="3"></i><b>(3)</b> RepresentationService.Context2 renderContext2, ObjectAndCollection objectAndCollection); <span class="annotation">@Programmatic</span> Response.ResponseBuilder buildResponse( <i class="conum" data-value="4"></i><b>(4)</b> RepresentationService.Context2 renderContext2, ObjectAndAction objectAndAction); <span class="annotation">@Programmatic</span> Response.ResponseBuilder buildResponse( <i class="conum" data-value="5"></i><b>(5)</b> RepresentationService.Context2 renderContext2, ObjectAndActionInvocation objectAndActionInvocation); }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>representation of a single object, as per section 14.4 of the RO spec, v1.0</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>representation of a single property of an object, as per section 16.4 of the RO spec v1.0</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>representation of a single collection of an object, as per section 17.5 of the RO spec v1.0</td> </tr> <tr> <td><i class="conum" data-value="4"></i><b>4</b></td> <td>representation of a single action (prompt) of an object, as per section 18.2 of the RO spec v1.0</td> </tr> <tr> <td><i class="conum" data-value="5"></i><b>5</b></td> <td>representation of the results of a single action invocation, as per section 19.5 of the RO spec v1.0</td> </tr> </table> </div> <div class="paragraph"> <p>These methods provide:</p> </div> <div class="ulist"> <ul> <li> <p>a <code>RepresentationService.Context2</code> which provides access to request-specific context (eg HTTP headers), session-specific context (eg authentication) and global context (eg configuration settings)</p> </li> <li> <p>an object representing the information to be rendered<br></p> <div class="paragraph"> <p>eg <code>ObjectAdapter</code>, <code>ObjectAndProperty</code>, <code>ObjectAndCollection</code> etc</p> </div> </li> </ul> </div> <div class="paragraph"> <p>In all cases, returning <code>null</code> will result in the regular RO spec representation being returned.</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>This is an "internal" SPI, meaning that it uses types that are not part of the Isis applib. We do not guarantee that <a href="http://semver.org">semantic versioning</a> will be honoured for these APIs.</p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_implementation_5">3.6.2. Implementation</h4> <div class="paragraph"> <p><code>ContentNegotiationServiceAbstract</code> (in <code>o.a.i.v.ro.rendering.service.conneg</code>) provides a no-op implementation of the SPI, along with supporting methods:</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">ContentNegotiationServiceAbstract</span> <span class="directive">implements</span> ContentNegotiationService { ... protected <span class="predefined-type">Object</span> objectOf(<span class="directive">final</span> ObjectAdapter objectAdapter) { ... } <span class="directive">protected</span> <span class="predefined-type">Object</span> returnedObjectOf(ObjectAndActionInvocation objectAndActionInvocation) { ... } <span class="directive">protected</span> <span class="predefined-type">Class</span>&lt;?&gt; loadClass(<span class="predefined-type">String</span> cls) { ... } <span class="directive">protected</span> <span class="type">void</span> ensureJaxbAnnotated(<span class="predefined-type">Class</span>&lt;?&gt; domainType) { ... } <span class="directive">protected</span> <span class="type">void</span> ensureDomainObjectAssignable( <span class="predefined-type">String</span> xRoDomainType, <span class="predefined-type">Class</span>&lt;?&gt; domainType, <span class="predefined-type">Object</span> domainObject) { ... } }</code></pre> </div> </div> <div class="paragraph"> <p>As discussed in the introduction, the framework also provides a default implementation, <code>o.a.i.v.ro.rendering.service.conneg.ContentNegotiationServiceXRoDomainType</code>. This handles content negotiation for two of the possible representations, object representations and for action result representations:</p> </div> <div class="ulist"> <ul> <li> <p>For object representations it will handle requests with HTTP <code>Accept</code> headers of the form:</p> <div class="ulist"> <ul> <li> <p><code>application/json;profile=urn:org.restfulobjects:repr-types/object;x-ro-domain-type=&#8230;&#8203;</code></p> </li> <li> <p><code>application/xml;profile=urn:org.restfulobjects:repr-types/object;x-ro-domain-type=&#8230;&#8203;</code></p> </li> </ul> </div> </li> <li> <p>for action result representations it will similarly handle requests with HTTP <code>Accept</code> headers of the form:</p> <div class="ulist"> <ul> <li> <p><code>application/json;profile=urn:org.restfulobjects:repr-types/action-result;x-ro-domain-type=&#8230;&#8203;</code><br></p> </li> <li> <p><code>application/xml;profile=urn:org.restfulobjects:repr-types/action-result;x-ro-domain-type=&#8230;&#8203;</code></p> </li> </ul> </div> </li> </ul> </div> <div class="paragraph"> <p>The value of the <code>x-ro-domain-type</code> parameter corresponds to the DTO to be mapped into by the <a href="#_rgsvc_spi_ContentMappingService"><code>ContentMappingService</code></a>.</p> </div> <div class="paragraph"> <p>If the DTO is annotated with JAXB, then also note that the runtime type must be annotated with the JAXB <code>javax.xml.bind.annotation.XmlRootElement</code> so that RestEasy is able to unambiguously serialize it.</p> </div> </div> <div class="sect3"> <h4 id="_usage_15">3.6.3. Usage</h4> <div class="paragraph"> <p>You can find an example of all these services in the (non-ASF) <a href="http://github.com/isisaddons/isis-app-todoapp">Isis addons' todoapp</a>. This defines a <code>ToDoItemDto</code> class that is JAXB annotated (it is in fact generated from an XSD).</p> </div> <div class="paragraph"> <p>The example app also includes an implementation of <code>ContentMappingService</code> that maps <code>todoapp.dom.module.todoitem.ToDoItem</code> entities to <code>todoapp.dto.module.todoitem.ToDoItemDto</code> classes.</p> </div> <div class="paragraph"> <p>A REST client can therefore request a DTO representation of an entity by invoking</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code>http://localhost:8080/restful/objects/TODO/0</code></pre> </div> </div> <div class="paragraph"> <p>with an <code>Accept</code> header of:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code>application/xml;profile=urn:org.restfulobjects:repr-types/object;x-ro-domain-type=todoapp.dto.module.todoitem.ToDoItemDto</code></pre> </div> </div> <div class="paragraph"> <p>will result in an XML serialization of that class:</p> </div> <div class="imageblock"> <div class="content"> <a class="image" href="images/reference-services-spi/ContentNegotiationService/accept-xml.png"><img src="images/reference-services-spi/ContentNegotiationService/accept-xml.png" alt="accept xml" width="700px"></a> </div> </div> <div class="paragraph"> <p>while similarly hitting the same URL with an <code>Accept</code> header of:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code>application/json;profile=urn:org.restfulobjects:repr-types/object;x-ro-domain-type=todoapp.dto.module.todoitem.ToDoItemDto</code></pre> </div> </div> <div class="paragraph"> <p>will result in the JSON serialization of that class:</p> </div> <div class="imageblock"> <div class="content"> <a class="image" href="images/reference-services-spi/ContentNegotiationService/accept-json.png"><img src="images/reference-services-spi/ContentNegotiationService/accept-json.png" alt="accept json" width="700px"></a> </div> </div> </div> <div class="sect3"> <h4 id="_configuration_3">3.6.4. Configuration</h4> <div class="paragraph"> <p>The default <code>ContentNegotiationServiceXRoDomainType</code> implementation provides a <a href="rgcfg.html#_rgcfg_configuring-core">configuration property</a> which controls whether a mapped domain object is pretty-printed (formatted, indented) or not:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="ini">isis.services.ContentNegotiationServiceXRoDomainType.prettyPrint=true</code></pre> </div> </div> <div class="paragraph"> <p>If the property is not set, then the default depends on the <a href="rgcfg.html#_rgcfg_deployment-types">deployment type</a>; production mode will disable pretty printing, while prototyping mode will enable it.</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_services_12">3.6.5. Registering the Services</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' default implementation of <code>ContentNegotiationService</code> service is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> <div class="sect3"> <h4 id="_related_services_15">3.6.6. Related Services</h4> <div class="paragraph"> <p>The default implementation of <code>ContentNegotiationService</code> delegates to <a href="#_rgsvc_spi_ContentMappingService"><code>ContentMappingService</code></a> (if present) to convert domain entities into a stable form (eg DTO).</p> </div> <div class="paragraph"> <p>The <code>ContentNegotiationService</code> is itself called by the (default implementation of) <a href="#_rgsvc_spi_RepresentationService"><code>RepresentationService</code></a>.</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_spi_EmailNotificationService">3.7. <code>EmailNotificationService</code></h3> <div class="paragraph"> <p>The <code>EmailNotificationService</code> supports the <a href="ugvw.html#_ugvw_features_user-registration">user registration</a> (self sign-up) features of the <a href="ugvw.html">Wicket viewer</a> whereby a user can sign-up to access an application by providing a valid email address.</p> </div> <div class="paragraph"> <p>The Wicket viewer will check whether an implementation of this service (and also the <a href="#_rgsvc_spi_UserRegistrationService"><code>UserRegistrationService</code></a>) is available, and if so will (unless configured not to) expose a sign-up page where the user enters their email address. A verification email is sent using this service; the email includes a link back to the running application. The user then completes the registration process (choosing a user name, password and so on) and the Wicket viewer creates an account for them (using the aforementioned <code>UserRegistrationService</code>).</p> </div> <div class="paragraph"> <p>The role of this service in all of this is to format and send out emails for the initial registration, or for password resets.</p> </div> <div class="paragraph"> <p>The default implementation of this service uses the <a href="#_rgsvc_api_EmailService"><code>EmailService</code></a>, which must be configured in order for user registration to be enabled.</p> </div> <div class="sect3"> <h4 id="_spi_7">3.7.1. SPI</h4> <div class="paragraph"> <p>The SPI defined by this service 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">interface</span> <span class="class">EmailNotificationService</span> <span class="directive">extends</span> <span class="predefined-type">Serializable</span> { <span class="annotation">@Programmatic</span> <span class="type">boolean</span> send(EmailRegistrationEvent ev); <i class="conum" data-value="1"></i><b>(1)</b> <span class="annotation">@Programmatic</span> <span class="type">boolean</span> send(PasswordResetEvent ev); <i class="conum" data-value="2"></i><b>(2)</b> <span class="annotation">@Programmatic</span> <span class="type">boolean</span> isConfigured(); <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>sends an email to verify an email address as part of the initial user registration</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>sends an email to reset a password for an already-registered user</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>determines whether the implementation was configured and initialized correctly</td> </tr> </table> </div> <div class="paragraph"> <p>If <code>isConfigured()</code> returns false then it is <em>not</em> valid to call <code>send(&#8230;&#8203;)</code> (and doing so will result in an <code>IllegalStateException</code> being thrown.</p> </div> </div> <div class="sect3"> <h4 id="_implementation_6">3.7.2. Implementation</h4> <div class="paragraph"> <p>The framework provides a default implementation, <code>o.a.i.core.runtime.services.userreg.EmailNotificationServiceDefault</code> that constructs the emails to send.</p> </div> <div class="sect4"> <h5 id="_alternative_implementations_3">Alternative Implementations</h5> <div class="paragraph"> <p>The text of these email templates is hard-coded as resources, in other words baked into the core jar files. If you need to use different text then you can of course always write and register your own implementation to be used instead of Isis' default.</p> </div> <div class="paragraph"> <p>If you have configured an alternative email service implementation, it should process the message body as HTML.</p> </div> <div class="paragraph"> <p>If you wish to write an alternative implementation of this service, note that (unlike most Apache Isis domain services) the implementation is also instantiated and injected by Google Guice. This is because <code>EmailNotificationService</code> is used as part of the <a href="ugvw.html#_ugvw_features_user-registration">user registration</a> functionality and is used by Wicket pages that are accessed outside of the usual Apache Isis runtime.</p> </div> <div class="paragraph"> <p>This implies a couple of additional constraints:</p> </div> <div class="ulist"> <ul> <li> <p>first, implementation class should also be annotated with <code>@com.google.inject.Singleton</code></p> </li> <li> <p>second, there may not be any Apache Isis session running. (If necessary, one can be created on the fly using <code>IsisContext.doInSession(&#8230;&#8203;)</code>)</p> </li> </ul> </div> <div class="paragraph"> <p>To ensure that your alternative implementation takes the place of the default implementation, register it explicitly in <code>isis.properties</code>.</p> </div> </div> </div> <div class="sect3"> <h4 id="_registering_the_service_16">3.7.3. Registering the Service</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' default implementation of <code>EmailNotificationService</code> service is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> <div class="sect3"> <h4 id="_related_services_16">3.7.4. Related Services</h4> <div class="paragraph"> <p>As noted elsewhere, the default implementation of this service uses <a href="#_rgsvc_api_EmailService"><code>EmailService</code></a>. This service has no specific configuration properties but does require that the <code>EmailService</code> has been configured.</p> </div> <div class="paragraph"> <p>Conversely, this service is used by (Isis' default implementation of) <a href="#_rgsvc_spi_UserRegistrationService"><code>UserRegistrationService</code></a>.</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_spi_ErrorReportingService">3.8. <code>ErrorReportingService</code></h3> <div class="paragraph"> <p>The <code>ErrorReportingService</code> service is an optional SPI that providies the ability to record any errors/exceptions that might occur in the application into an external incident recording system (such as JIRA). The service also allows a user-friendly (jargon-free) error message to be returned and rendered to the end-user, along with an optional incident reference (eg a JIRA issue <code>XXX-1234</code>).</p> </div> <div class="sect3"> <h4 id="_spi_8">3.8.1. SPI</h4> <div class="paragraph"> <p>The SPI defined by this service 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">interface</span> <span class="class">ErrorReportingService</span> { Ticket reportError(ErrorDetails errorDetails); }</code></pre> </div> </div> <div class="paragraph"> <p>where <code>ErrorDetails</code> provided to the service 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">ErrorDetails</span> { <span class="directive">public</span> <span class="predefined-type">String</span> getMainMessage() { ... } <i class="conum" data-value="1"></i><b>(1)</b> <span class="directive">public</span> <span class="type">boolean</span> isRecognized() { ... } <i class="conum" data-value="2"></i><b>(2)</b> <span class="directive">public</span> <span class="type">boolean</span> isAuthorizationCause() { ... } <i class="conum" data-value="3"></i><b>(3)</b> <span class="directive">public</span> <span class="predefined-type">List</span>&lt;<span class="predefined-type">String</span>&gt; getStackTraceDetailList() { <i class="conum" data-value="4"></i><b>(4)</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 main message to be displayed to the end-user. The service is responsible for translating this into the language of the end-user (it can use <a href="#_rgsvc_spi_LocaleProvider"><code>LocaleProvider</code></a> if required).</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>whether this message has already been recognized by an <a href="#_rgsvc_spi_ExceptionRecognizer"><code>ExceptionRecognizer</code></a> service. Generally this converts potentially non-recoverable (fatal) exceptions into recoverable exceptions (warnings) as well providing an alternative mechanism for generating user-friendly error messages.</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>whether the cause of the exception was due to a lack of privileges. In such cases the UI restricts the information shown to the end-user, to avoid leaking potentially sensitive information</td> </tr> <tr> <td><i class="conum" data-value="4"></i><b>4</b></td> <td>the stack trace of the exception, including the trace of any exceptions in the causal chain. These technical details are hidden from the user and only shown for non-recoverable exceptions.</td> </tr> </table> </div> <div class="paragraph"> <p>and <code>Ticket</code> (returned by the service) has the constructor:</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">Ticket</span> <span class="directive">implements</span> <span class="predefined-type">Serializable</span> { <span class="directive">public</span> Ticket( <span class="directive">final</span> <span class="predefined-type">String</span> reference, <i class="conum" data-value="1"></i><b>(1)</b> <span class="directive">final</span> <span class="predefined-type">String</span> userMessage, <i class="conum" data-value="2"></i><b>(2)</b> <span class="directive">final</span> <span class="predefined-type">String</span> details) { ... } <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 a unique identifier that the end-user can use to track any follow-up from this error. For example, an implementation might automatically log an issue in a bug tracking system such as JIRA, in which case the reference would probably be the JIRA issue number &lt;tt&gt;XXX-1234&lt;/tt&gt;.</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>a short, jargon-free message to display to the end-user.</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>is optional additional details to show. For example, these might include text on how to recover from the error, or workarounds, or just further details on contacting the help desk if the issue is severe and requires immediate attention.</td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_implementation_7">3.8.2. Implementation</h4> <div class="paragraph"> <p>The (non-ASF) <a href="http://github.com/isisaddons/isis-app-kitchensink">Isis addons' kitchensink</a> app provides an example implementation:</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">KitchensinkErrorReportingService</span> <span class="directive">implements</span> ErrorReportingService { <span class="directive">private</span> <span class="type">int</span> ticketNumber = <span class="integer">1</span>; <span class="annotation">@Override</span> <span class="directive">public</span> Ticket reportError(<span class="directive">final</span> ErrorDetails errorDetails) { <span class="keyword">return</span> <span class="keyword">new</span> Ticket( nextTicketReference(), <span class="string"><span class="delimiter">&quot;</span><span class="content">The Kitchen sink app is sorry to report that: </span><span class="delimiter">&quot;</span></span> + errorDetails.getMainMessage(), <span class="string"><span class="delimiter">&quot;</span><span class="content">These are additional details for the end-user to read.</span><span class="char">\n</span><span class="delimiter">&quot;</span></span> + <span class="string"><span class="delimiter">&quot;</span><span class="content">This content should be able to span many lines.</span><span class="char">\n</span><span class="delimiter">&quot;</span></span> + <span class="string"><span class="delimiter">&quot;</span><span class="content">More detail.</span><span class="char">\n</span><span class="delimiter">&quot;</span></span> + <span class="string"><span class="delimiter">&quot;</span><span class="content">Some suggested work-arounds.</span><span class="char">\n</span><span class="delimiter">&quot;</span></span> + <span class="string"><span class="delimiter">&quot;</span><span class="content">Details of how to contact help desk.</span><span class="char">\n</span><span class="delimiter">&quot;</span></span> + <span class="string"><span class="delimiter">&quot;</span><span class="content">And so on</span><span class="delimiter">&quot;</span></span>); } <span class="predefined-type">String</span> nextTicketReference() { <span class="keyword">return</span> <span class="string"><span class="delimiter">&quot;</span><span class="delimiter">&quot;</span></span> + ticketNumber++; } }</code></pre> </div> </div> <div class="paragraph"> <p>which is rendered as:</p> </div> <div class="imageblock"> <div class="content"> <a class="image" href="images/reference-services-spi/ErrorReportingService/kitchensink-example.png"><img src="images/reference-services-spi/ErrorReportingService/kitchensink-example.png" alt="kitchensink example" width="860px"></a> </div> </div> </div> <div class="sect3"> <h4 id="_registering_the_services_13">3.8.3. Registering the Services</h4> <div class="paragraph"> <p>There is no default implementation of this service. To register your own implementation (and assuming that an <code>AppManifest</code> is being used to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>), then just ensure that the implementation is on the classpath and the module containing the implementation is returned in <code>AppManifest#getModules()</code>.</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_spi_EventSerializer">3.9. <code>EventSerializer</code></h3> <div class="paragraph"> <p>The <code>EmailSerializer</code> service is a supporting service intended for use by (any implementation of) <a href="#_rgsvc_spi_PublishingService"><code>PublishingService</code></a>. Its responsibility is to combine the <code>EventMetadata</code> and the <code>EventPayload</code> into some serialized form (such as JSON, XML or a string) that can then be published.</p> </div> <div class="paragraph"> <p>See <a href="#_rgsvc_spi_PublishingService"><code>PublishingService</code></a> for further discussion.</p> </div> <div class="sect3"> <h4 id="_spi_9">3.9.1. SPI</h4> <div class="paragraph"> <p>The SPI defined by this service 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">interface</span> <span class="class">EventSerializer</span> { <span class="predefined-type">Object</span> serialize( <i class="conum" data-value="1"></i><b>(1)</b> EventMetadata metadata, <i class="conum" data-value="2"></i><b>(2)</b> EventPayload payload); <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>returns an object for maximum flexibility, which is then handed off to the <code>PublishingService</code>.</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>standard metadata about the event, such as the user, the <a href="rgcms.html#_rgcms_classes_mixins_HasTransactionId"><code>transactionId</code></a>, date/time etc</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>for published actions, will generally be an <code>EventPayloadForActionInvocation</code> (or subclass thereof); for published objects, will generally be an <code>EventPayloadForObjectChanged</code> (or subclass thereof)</td> </tr> </table> </div> <div class="paragraph"> <p>It&#8217;s important to make sure that the publishing service implementation is able to handle the serialized form. Strings are a good lowest common denominator, but in some cases a type-safe equivalent, such as a w3c DOM <code>Document</code> or JSON node might be passed instead.</p> </div> </div> <div class="sect3"> <h4 id="_implementation_8">3.9.2. Implementation</h4> <div class="paragraph"> <p>The (non-ASF) <a href="http://github.com/isisaddons/isis-module-publishing">Isis addons' publishing</a> module provides an implementation (<code>org.isisaddons.module.publishing.dom.eventserializer.RestfulObjectsSpecEventSerializer</code>) that represents the event payload using the representation defined by the <a href="http://restfulobjects.org">Restful Objects spec</a> of (transient) objects, grafting on the metadata as additional JSON nodes.</p> </div> <div class="paragraph"> <p>For example, this is the JSON generated on an action invocation:</p> </div> <div class="imageblock"> <div class="content"> <a class="image" href="images/reference-services-spi/EventSerializer/action-invocation-published-to-stderr.png"><img src="images/reference-services-spi/EventSerializer/action-invocation-published-to-stderr.png" alt="action invocation published to stderr" width="750px"></a> </div> <div class="title">Figure 1. JSON representation of a published action invocation</div> </div> <div class="paragraph"> <p>while this is the object change JSON:</p> </div> <div class="imageblock"> <div class="content"> <a class="image" href="images/reference-services-spi/EventSerializer/changed-object-published-to-stderr.png"><img src="images/reference-services-spi/EventSerializer/changed-object-published-to-stderr.png" alt="changed object published to stderr" width="750px"></a> </div> <div class="title">Figure 2. JSON representation of a published changed object</div> </div> <div class="paragraph"> <p>You could if you wish change the representation by registering your own implementation of this API in <code>isis.properties</code>:</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_services_14">3.9.3. Registering the Services</h4> <div class="paragraph"> <p>There is no default implementation of this service provided by the core Apache Isis framework.</p> </div> <div class="paragraph"> <p>The (non-ASF) <a href="http://github.com/isisaddons/isis-module-publishing">Isis addons' publishing</a> module provides an implementation of this service (<code>RestfulObjectsSpecEventSerializer</code>) that serializes action invocations and published objects into a format based on the Restful Objects specification. It also (as you might imagine) provides an implementation of the <a href="#_rgsvc_spi_PublishingService"><code>PublishingService</code></a>.</p> </div> <div class="paragraph"> <p>Assuming that an <code>AppManifest</code> is being used to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then this can be activated by updating the <code>pom.xml</code> and updating the <code>AppManifest#getModules()</code> method.</p> </div> </div> <div class="sect3"> <h4 id="_related_services_17">3.9.4. Related Services</h4> <div class="paragraph"> <p>This service is intended (though not mandated) to be used by implementations of <a href="#_rgsvc_spi_PublishingService"><code>PublishingService</code></a>. The (non-ASF) <a href="http://github.com/isisaddons/isis-module-publishing">Isis addons' publishing</a> module does use it (though the (non-ASF) <a href="http://github.com/isisaddons/isis-module-publishmq">Isis addons' publishmq</a> module does not).</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_spi_ExceptionRecognizer">3.10. <code>ExceptionRecognizer</code></h3> <div class="paragraph"> <p>The <code>ExceptionRecognizer</code> service provides the mechanism for both the domain programmer and also for components to be able to recognize and handle certain exceptions that may be thrown by the system. Rather than display an obscure error to the end-user, the application can instead display a user-friendly message.</p> </div> <div class="paragraph"> <p>For example, the JDO/DataNucleus Objectstore provides a set of recognizers to recognize and handle SQL constraint exceptions such as uniqueness violations. These can then be rendered back to the user as expected errors, rather than fatal stacktraces.</p> </div> <div class="paragraph"> <p>It is also possible to provide additional implementations, registered in <code>isis.properties</code>. Unlike other services, where any service registered in <code>isis.properties</code> replaces any default implementations, in the case of this service all implementations registered are "consulted" to see if they recognize an exception (the chain-of-responsibility pattern).</p> </div> <div class="sect3"> <h4 id="_spi_10">3.10.1. SPI</h4> <div class="paragraph"> <p>The SPI defined by this service 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">interface</span> <span class="class">ExceptionRecognizer2</span> ... { <span class="directive">public</span> <span class="type">enum</span> Category { <i class="conum" data-value="1"></i><b>(1)</b> ... } <span class="directive">public</span> <span class="directive">static</span> <span class="type">class</span> <span class="class">Recognition</span> { <i class="conum" data-value="2"></i><b>(2)</b> <span class="directive">private</span> Category category; <span class="directive">private</span> <span class="predefined-type">String</span> reason; ... } <span class="annotation">@Programmatic</span> <span class="directive">public</span> Recognition recognize2(<span class="predefined-type">Throwable</span> ex); <i class="conum" data-value="3"></i><b>(3)</b> <span class="annotation">@Deprecated</span> <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="predefined-type">String</span> recognize(<span class="predefined-type">Throwable</span> ex); <i class="conum" data-value="4"></i><b>(4)</b> }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>an enumeration of varies categories of exceptions that are recognised</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>represents the fact that an exception has been recognized as has been converted into a user-friendy message, and has been categorized</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>the main API, to attempt to recognize an exception</td> </tr> <tr> <td><i class="conum" data-value="4"></i><b>4</b></td> <td>deprecated API which converted exceptions into strings (reasons), ie without any categorization. This is no longer called.</td> </tr> </table> </div> <div class="paragraph"> <p>The categories are:</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">ExceptionRecognizer2</span> ... { <span class="directive">public</span> <span class="type">enum</span> Category { CONSTRAINT_VIOLATION, <i class="conum" data-value="1"></i><b>(1)</b> NOT_FOUND, <i class="conum" data-value="2"></i><b>(2)</b> CONCURRENCY, <i class="conum" data-value="3"></i><b>(3)</b> CLIENT_ERROR, <i class="conum" data-value="4"></i><b>(4)</b> SERVER_ERROR, <i class="conum" data-value="5"></i><b>(5)</b> OTHER <i class="conum" data-value="6"></i><b>(6)</b> } ... }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>a violation of some declarative constraint (eg uniqueness or referential integrity) was detected.</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>the object to be acted upon cannot be found (404)</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>a concurrency exception, in other words some other user has changed this object.</td> </tr> <tr> <td><i class="conum" data-value="4"></i><b>4</b></td> <td>recognized, but for some other reason&#8230;&#8203; 40x error</td> </tr> <tr> <td><i class="conum" data-value="5"></i><b>5</b></td> <td>50x error</td> </tr> <tr> <td><i class="conum" data-value="6"></i><b>6</b></td> <td>recognized, but uncategorized (typically: a recognizer of the original <code>ExceptionRecognizer</code> API).</td> </tr> </table> </div> <div class="paragraph"> <p>In essence, if an exception is recognized then it is also categorized. This lets the viewer act accordingly. For example, if an exception is raised from the loading of an individual object, then this is passed by the registered <code>ExceptionRecognizer</code>s. If any of these recognize the exception as representing a not-found exception, then an Apache Isis <code>ObjectNotFoundException</code> is raised. Both the viewers interprets this correctly (the <a href="ugvw.html">Wicket viewer</a> as a suitable error page, the <a href="ugvro.html">Restful Objects viewer</a> as a 404 status return code).</p> </div> <div class="paragraph"> <p>If the implementation recognizes the exception then it returns a user-friendly message to be rendered (by the viewer) back to the user; otherwise it returns <code>null</code>. There is no need for the implementation to check for exception causes; the casual chain is unwrapped by Apache Isis core and each exception in the chain will also be passed through to the recognizer (from most specific to least). The recognizer implementation can therefore be as fine-grained or as coarse-grained as it wishes.</p> </div> </div> <div class="sect3"> <h4 id="_implementation_9">3.10.2. Implementation</h4> <div class="paragraph"> <p>The framework provides two default implementations:</p> </div> <div class="ulist"> <ul> <li> <p><code>o.a.i.core.metamodel.services.container.DomainObjectContainerDefault</code> provided by Apache Isis core is itself an <code>ExceptionRecognizer</code>, and will handle <code>ConcurrencyException</code>s. It will also handle any application exceptions raised by the system (subclasses of <code>o.a.i.applib.RecoverableException</code>).</p> </li> <li> <p><code>o.a.i.objectstore.jdo.applib.service.exceprecog.ExceptionRecognizerCompositeForJdoObjectStore</code> bundles up a number of more fine-grained implementations:</p> <div class="ulist"> <ul> <li> <p><code>ExceptionRecognizerForSQLIntegrityConstraintViolationUniqueOrIndexException</code></p> </li> <li> <p><code>ExceptionRecognizerForJDOObjectNotFoundException</code></p> </li> <li> <p><code>ExceptionRecognizerForJDODataStoreException</code></p> </li> </ul> </div> </li> </ul> </div> <div class="paragraph"> <p>If you want to recognize and handle additional exceptions (for example to capture error messages specific to the JDBC driver you might be using), then create a fine-grained implementation of <code>ExceptionRecognizer2</code> for the particular error message (there are some convenience implementations of the interface that you can subclass from if required) and register in <code>isis.properties</code>.</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_services_15">3.10.3. Registering the Services</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then the default implementations provided by the framework (<code>DomainObjectContainerDefault</code> and <code>ExceptionRecognizerCompositeForJdoObjectStore</code>) will be registered.</p> </div> <div class="paragraph"> <p>In addition, you can register any further exception recognizers in <code>isis.properties</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="ini">isis.services=...,\ com.mycompany.myapp.MyExceptionRecognizer,\ ...</code></pre> </div> </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>Prior to 1.9.0, the <code>ExceptionRecognizerCompositeForJdoObjectStore</code> also required manual registration.</p> </div> </td> </tr> </table> </div> <div class="paragraph"> <p>If the JDO exception recognizers are not required (rather unlikely), then they can be disabled en-masse using the <a href="rgcfg.html#_rgcfg_configuring-core">configuration property</a> <code>isis.services.ExceptionRecognizerCompositeForJdoObjectStore.disable</code>.</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_spi_FixtureScriptsSpecificationProvider">3.11. <code>FixtureScriptsSpecificationProvider</code></h3> <div class="paragraph"> <p>The <code>FixtureScriptsSpecificationProvider</code> configures the <a href="#_rgsvc_api_FixtureScriptsDefault"><code>FixtureScriptsDefault</code></a> domain service, providing the location to search for fixture scripts and other settings.</p> </div> <div class="paragraph"> <p>The service is only used if the <code>FixtureScriptsDefault</code> service is instantiated as a fallback by the framework. If the application provides its own subclass of <a href="rgcms.html#_rgcms_classes_super_FixtureScripts"><code>FixtureScripts</code></a> superclass, then this provider service is not used.</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>Of the two designs, we encourage you to implement this "provider" SPI rather than subclass <code>FixtureScripts</code>. The primary benefit (apart from decoupling responsibilities) is that it ensures that there is always an instance of <code>FixtureScripts</code> available for use.</p> </div> </td> </tr> </table> </div> <div class="sect3"> <h4 id="_spi_11">3.11.1. SPI</h4> <div class="paragraph"> <p>The SPI defined by the service 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">interface</span> <span class="class">FixtureScriptsSpecificationProvider</span> { <span class="annotation">@Programmatic</span> FixtureScriptsSpecification getSpecification(); }</code></pre> </div> </div> <div class="paragraph"> <p>where <code>FixtureScriptsSpecification</code> exposes these values:</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">FixtureScriptsSpecification</span> { <span class="directive">public</span> <span class="predefined-type">String</span> getPackagePrefix() { ... } <span class="directive">public</span> FixtureScripts.NonPersistedObjectsStrategy getNonPersistedObjectsStrategy() { ... } <span class="directive">public</span> FixtureScripts.MultipleExecutionStrategy getMultipleExecutionStrategy() { ... } <span class="directive">public</span> <span class="predefined-type">Class</span>&lt;? <span class="directive">extends</span> FixtureScript&gt; getRunScriptDefaultScriptClass() { ... } <span class="directive">public</span> DropDownPolicy getRunScriptDropDownPolicy() { ... } <span class="directive">public</span> <span class="predefined-type">Class</span>&lt;? <span class="directive">extends</span> FixtureScript&gt; getRecreateScriptClass() { ... } ... }</code></pre> </div> </div> <div class="paragraph"> <p>The class is immutable but it has a builder (obtained using <code>FixturescriptsSpecification.builder(&#8230;&#8203;)</code>) for a fluent API.</p> </div> </div> <div class="sect3"> <h4 id="_implementation_10">3.11.2. Implementation</h4> <div class="paragraph"> <p>The <a href="ugfun.html#_ugfun_getting-started_simpleapp-archetype">SimpleApp archetype</a> has a simple implementation of this service:</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">DomainAppFixturesProvider</span> <span class="directive">implements</span> FixtureScriptsSpecificationProvider { <span class="annotation">@Override</span> <span class="directive">public</span> FixtureScriptsSpecification getSpecification() { <span class="keyword">return</span> FixtureScriptsSpecification .builder(DomainAppFixturesProvider.class) .with(FixtureScripts.MultipleExecutionStrategy.EXECUTE) .withRunScriptDefault(RecreateSimpleObjects.class) .withRunScriptDropDown(FixtureScriptsSpecification.DropDownPolicy.CHOICES) .withRecreate(RecreateSimpleObjects.class) .build(); } }</code></pre> </div> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_spi_LocaleProvider">3.12. <code>LocaleProvider</code></h3> <div class="paragraph"> <p>The <code>LocaleProvider</code> service is one of the services that work together to implement Apache Isis' support for i18n, being used by Isis' default implementation of <a href="#_rgsvc_spi_TranslationService"><code>TranslationService</code></a>.</p> </div> <div class="paragraph"> <p>The role of the service itself is simply to return the <code>Locale</code> of the current user.</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>For the "big picture" and further details on Apache Isis' i18n support, see <a href="ugbtb.html#_ugbtb_i18n">here</a>.</p> </div> </td> </tr> </table> </div> <div class="sect3"> <h4 id="_spi_12">3.12.1. SPI</h4> <div class="paragraph"> <p>The SPI defined by this service 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">interface</span> <span class="class">LocaleProvider</span> { <span class="annotation">@Programmatic</span> <span class="predefined-type">Locale</span> getLocale(); }</code></pre> </div> </div> <div class="paragraph"> <p>This is notionally request-scoped, returning the <code>Locale</code> of the current user; <em>not</em> that of the server. (Note that the implementation is not required to actually be <a href="rgant.html#_rgant-RequestScoped"><code>@RequestScoped</code></a>, however).</p> </div> </div> <div class="sect3"> <h4 id="_implementation_11">3.12.2. Implementation</h4> <div class="paragraph"> <p>Isis' <a href="ugvw.html">Wicket viewer</a> provides an implementation of this service (<code>LocaleProviderWicket</code>) which leverages Apache Wicket APIs.</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>Currently there is no equivalent implementation for the <a href="ugvro.html">RestfulObjects viewer</a>.</p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_registering_the_services_16">3.12.3. Registering the Services</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#<em>rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>), _and</em> that the <a href="ugvw.html">Wicket viewer</a> is being used, then an implementation of <code>LocaleProvider</code> is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> <div class="sect3"> <h4 id="_related_services_18">3.12.4. Related Services</h4> <div class="paragraph"> <p>This service works in conjunction with <a href="#_rgsvc_spi_TranslationService"><code>TranslationService</code></a> and <a href="#_rgsvc_spi_TranslationsResolver"><code>TranslationsResolver</code></a> in order to provide i18n support.</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_spi_PublishingService">3.13. <code>PublishingService</code></h3> <div class="paragraph"> <p>The <code>PublishingService</code> is intended for coarse-grained publish/subscribe for system-to-system interactions, from Apache Isis to some other system. Here the only events published are those that action invocations and of changed objects. A typical use case is to publish onto a pub/sub bus such as <a href="http://activemq.apache.org/">ActiveMQ</a> with <a href="http://camel.apache.org">Camel</a> to keep other systems up to date.</p> </div> <div class="sect3"> <h4 id="_spi_13">3.13.1. SPI</h4> <div class="paragraph"> <p>The SPI defined by the service 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">interface</span> <span class="class">PublishingService</span> { <span class="directive">public</span> <span class="type">void</span> publish( EventMetadata metadata, <i class="conum" data-value="1"></i><b>(1)</b> EventPayload payload); <i class="conum" data-value="2"></i><b>(2)</b> <span class="annotation">@Deprecated</span> <span class="annotation">@Programmatic</span> <span class="type">void</span> setEventSerializer(EventSerializer eventSerializer); <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>standard metadata about the event, such as the user, the <a href="rgcms.html#_rgcms_classes_mixins_HasTransactionId"><code>transactionId</code></a>, date/time etc</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>for published actions, an <code>EventPayloadForActionInvocation</code> (or subclass thereof); for published objects, an <code>EventPayloadForObjectChanged</code> (or subclass thereof)</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>injects in the <a href="#_rgsvc_spi_EventSerializer"><code>EventSerializer</code></a> service. This is deprecated because not every implementation is required to use an <code>EventSerializer</code> so its inclusion within the SPI of <code>PublishingService</code> was in retrospect a mistake.</td> </tr> </table> </div> <div class="paragraph"> <p>Typically implementations will use the injected <code>EventSerializer</code> to convert the metadata and payload into a form to be published:</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">EventSerializer</span> { <span class="directive">public</span> <span class="predefined-type">Object</span> serialize(EventMetadata metadata, EventPayload payload); }</code></pre> </div> </div> <div class="paragraph"> <p>The serialized form returned by <code>EventSerializer</code> must be in a form that the <code>PublishingService</code> implementation is able to handle. Strings are a good lowest common denominator, but (if custom implementations of both <code>EventSerializer</code> and <code>PublishingService</code> were in use) then it might also be some other type, for example an <code>org.w3c.dom.Document</code> or an <code>org.json.JSONObject</code> might be returned instead.</p> </div> </div> <div class="sect3"> <h4 id="_implementation_12">3.13.2. Implementation</h4> <div class="paragraph"> <p>The framework provides no default implementations of this service. There are however two implementations available in the (non-ASF) <a href="http://isisaddons.org">Isis Addons</a>.</p> </div> <div class="sect4"> <h5 id="_isis_module_publishing">isis-module-publishing</h5> <div class="paragraph"> <p>The (non-ASF) <a href="http://github.com/isisaddons/isis-module-publishing">Isis addons' publishing</a> module provides an implementation (<code>org.isisaddons.module.publishing.dom.PublishingService</code>) that persists each event as a <code>PublishedEvent</code> entity. This holds the serialized form of the event metadata and payload as translated into a string by the injected <code>EventSerializer</code>. The module also provides its own implementation of <code>EventSerializer</code>, namely <code>RestfulObjectsSpecEventSerializer</code>, which represents the event payload using the representation defined by the <a href="http://restfulobjects.org">Restful Objects spec</a> of (transient) objects, grafting on the metadata as additional JSON nodes.</p> </div> <div class="paragraph"> <p>The <code>PublishedEvent</code> entity also has a <code>state</code> field taking the values either "QUEUED" or "PROCESSED". The intention here is that an event bus can poll this table to grab pending events and dispatch them to downstream systems. When <code>PublishedEvent</code>s are persisted initially they always take the value "QUEUED".</p> </div> </div> <div class="sect4"> <h5 id="_isis_module_publishmq">isis-module-publishmq</h5> <div class="paragraph"> <p>The (non-ASF) <a href="http://github.com/isisaddons/isis-module-publishmq">Isis addons' publishmq</a> module provides an implementation (<code>org.isisaddons.module.publismq.dom.servicespi.PublishingServiceUsingActiveMq</code>) that publishes each action invocation as an event on an <a href="http://activemq.apache.org">ActiveMQ</a> message queue. These are converted into a canonical XML form (the <a href="rgcms.html#_rgcms_schema-aim">ActionInvocationMemento</a> schema) using the <code>ActionInvocationMementoDtoUtils</code> class; the idea being that subscribers on the ActiveMQ message queue can then query back for further information, for example using the <a href="ugvro.html">RestfulObjects viewer</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>At the time of writing this implementation does not publish changed object events.</p> </div> </td> </tr> </table> </div> </div> </div> <div class="sect3"> <h4 id="_usage_16">3.13.3. Usage</h4> <div class="paragraph"> <p>To indicate that an action invocation should be published, annotate it with the <a href="rgant.html#_rgant-Action_publishing"><code>@Action#publishing()</code></a> annotation.</p> </div> <div class="paragraph"> <p>To indicate that a changed object should be published is to annotate it with the <a href="rgant.html#_rgant-DomainObject_publishing"><code>@DomainObject#publishing()</code></a> annotation.</p> </div> <div class="paragraph"> <p>It is also possible to "fine-tune" the <code>EventPayload</code> using the <code>#publishingFactory()</code> attribute (for both annotations). By default the <code>EventPayload</code> that is serialized identifies the object(s) being interacted with or changed, and in the case of the action invocation provides details of the action arguments and result (if any) of that action. However, the payload does not (by default) include any information about the new state of these objects. It is therefore the responsibility of the subscriber to call back to Apache Isis to determine any information that has not been published.</p> </div> <div class="paragraph"> <p>Although the representations (if using the Restful Object serializer and Restful Objects viewer) does include hrefs for the objects, this nevertheless requires an additional network call to obtain this information).</p> </div> <div class="paragraph"> <p>In some circumstances, then, it may make more sense to eagerly "push" information about the change to the subscriber by including that state within the payload.</p> </div> <div class="paragraph"> <p>To accomplish this, an implementation of a &#8220;PayloadFactory&#8221; must be specified in the annotation.</p> </div> <div class="paragraph"> <p>For actions, we implement the <code>PublishingPayloadFactoryForAction</code> (in <code>o.a.i.applib.annotation</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">PublishingPayloadFactoryForAction</span> { <span class="annotation">@Programmatic</span> <span class="directive">public</span> EventPayload payloadFor( Identifier actionIdentifier, <span class="predefined-type">Object</span> target, <span class="predefined-type">List</span>&lt;<span class="predefined-type">Object</span>&gt; arguments, <span class="predefined-type">Object</span> result); } }</code></pre> </div> </div> <div class="paragraph"> <p>The <code>EventPayloadForActionInvocation</code> abstract class (in the Isis applib) should be used as the base class for the object instance returned from <code>payLoadFor(&#8230;&#8203;)</code>.</p> </div> <div class="paragraph"> <p>For objects, the interface to implement is <code>PublishingPayloadFactoryForObject</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">PublishingPayloadFactoryForObject</span> { <span class="annotation">@Programmatic</span> <span class="directive">public</span> EventPayload payloadFor( <span class="predefined-type">Object</span> changedObject, PublishingChangeKind publishingChangeKind); <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>an enum taking the values <code>CREATE</code>, <code>UPDATE</code>, <code>DELETE</code></td> </tr> </table> </div> <div class="paragraph"> <p>Similarly, the <code>EventPayloadForObjectChanged</code> abstract class should be used as the base class for the object returned from <code>payLoadFor(&#8230;&#8203;)</code>.</p> </div> <div class="paragraph"> <p>For example, the following will eagerly include a <code>ToDoItem&#8217;s `description</code> property whenever it is changed:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@DomainObject</span>(publishingPayloadFactory=ToDoItemPayloadFactory.class) <span class="directive">public</span> <span class="type">class</span> <span class="class">ToDoItem</span> { ... }</code></pre> </div> </div> <div class="paragraph"> <p>where <code>ToDoItemPayloadFactory</code> is defined as:</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">ToDoItemChangedPayloadFactory</span> <span class="directive">implements</span> PublishingPayloadFactoryForObject { <span class="directive">public</span> <span class="directive">static</span> <span class="type">class</span> <span class="class">ToDoItemPayload</span> <span class="directive">extends</span> EventPayloadForObjectChanged&lt;ToDoItem&gt; { <span class="directive">public</span> ToDoItemPayload(ToDoItem changed) { <span class="local-variable">super</span>(changed); } <span class="directive">public</span> <span class="predefined-type">String</span> getDescription() { <span class="keyword">return</span> getChanged().getDescription(); } } <span class="annotation">@Override</span> <span class="directive">public</span> EventPayload payloadFor(<span class="predefined-type">Object</span> changedObject, PublishingChangeKind kind) { <span class="keyword">return</span> <span class="keyword">new</span> ToDoItemPayload((ToDoItem) changedObject); } }</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_registering_the_services_17">3.13.4. Registering the Services</h4> <div class="paragraph"> <p>There is no default implementation of this service provided by the core Apache Isis framework.</p> </div> <div class="paragraph"> <p>Both the (non-ASF) Isis addons' <a href="http://github.com/isisaddons/isis-module-publishing">publishing</a> module and the <a href="http://github.com/isisaddons/isis-module-publishmq">publishmq</a> module provide implementations of this service. Assuming that an <code>AppManifest</code> is being used to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then this can be activated by updating the <code>pom.xml</code> and updating the <code>AppManifest#getModules()</code> method.</p> </div> <div class="paragraph"> <p>The modules also provide services that contribute to the UI. If contributions are not required in the UI, these can be suppressed either using security or by implementing a <a href="ugbtb.html#_ugbtb_decoupling_vetoing-visibility">vetoing subscriber</a>.</p> </div> </div> <div class="sect3"> <h4 id="_related_services_19">3.13.5. Related Services</h4> <div class="paragraph"> <p>The <code>PublishingService</code> is intended for coarse-grained publish/subscribe for system-to-system interactions, from Apache Isis to some other system. Here the only events published are those that action invocations (for actions annotated with <a href="rgant.html#_rgant-Action_publishing"><code>@Action#publishing()</code></a>) and of changed objects (for objects annotated with <a href="rgant.html#_rgant-DomainObject_publishing"><code>@DomainObject#publishing()</code></a>.</p> </div> <div class="paragraph"> <p>The <a href="#_rgsvc_api_EventBusService"><code>EventBusService</code></a> meanwhile is intended for fine-grained publish/subscribe for object-to-object interactions within an Apache Isis domain object model. The event propagation is strictly in-memory, and there are no restrictions on the object acting as the event (it need not be serializable, for example).</p> </div> <div class="paragraph"> <p>All three of these services collaborate implicitly by way of the <a href="rgcms.html#_rgcms_classes_mixins_HasTransactionId"><code>HasTransactionId</code></a> interface.</p> </div> </div> <div class="sect3"> <h4 id="_design_notes">3.13.6. Design Notes</h4> <div class="paragraph"> <p>The following class diagram shows how the above components fit together:</p> </div> <div class="imageblock"> <div class="content"> <a class="image" href="images/reference-services-spi/PublishingService/yuml.me-23db58a4.png"><img src="images/reference-services-spi/PublishingService/yuml.me-23db58a4.png" alt="yuml.me 23db58a4" width="800px"></a> </div> </div> <div class="paragraph"> <p>This yuml.me diagram was generated at <a href="http://yuml.me/edit/23db58a4">yuml.me</a>.</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_spi_RepresentationService">3.14. <code>RepresentationService</code></h3> <div class="paragraph"> <p>The <code>RepresentationService</code> is the main plug-in point for the <a href="ugvro.html">RestfulObjects viewer</a> to generate representations.</p> </div> <div class="paragraph"> <p>The default implementation generates representations according to the <a href="http://restfulobjects.org">Restful Objects spec</a> v1.0. However, it also delegates to the <a href="#_rgsvc_spi_ContentNegotiationService"><code>ContentNegotiationService</code></a> which provides a mechanism for altering representations according to the HTTP <code>Accept</code> header.</p> </div> <div class="paragraph"> <p>The principal motivation is to allow more flexible representations to be generated for REST clients that (perhaps through their use of a certain Javascript library, say) expect, or at least works best with, a certain style of representation.</p> </div> <div class="paragraph"> <p>In all there are three domain services that can influence the representations generated: this service, <a href="#_rgsvc_spi_ContentNegotiationService"><code>ContentNegotiationService</code></a>and the <a href="#_rgsvc_spi_ContentMappingService"><code>ContentMappingService</code></a>. The diagram below shows how these collaborate:</p> </div> <div class="imageblock"> <div class="content"> <a class="image" href="images/reference-services-spi/RepresentationService/service-collaborations.png"><img src="images/reference-services-spi/RepresentationService/service-collaborations.png" alt="service collaborations" width="700px"></a> </div> </div> <div class="paragraph"> <p>The <code>RepresentationServiceForRestfulObjects</code> is the default implementation of this service; likewise <code>ContentNegotiationServiceXRoDomainType</code> is the default implementation of the <code>ContentNegotiationService</code>. If you inspect the source code you&#8217;ll see that the default implementation of this service&#8217;s primary responsibility is to generate the default Restful Objects representations. Therefore, if you what you want to do is to generate a <em>different _representation then in many cases replacing either this service _or</em> the <code>ContentNegotiationService</code> will be equivalent (you&#8217;ll notice that their SPIs are very similar).</p> </div> <div class="sect3"> <h4 id="_spi_14">3.14.1. SPI</h4> <div class="paragraph"> <p>The SPI defined by this service 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">interface</span> <span class="class">RepresentationService</span> { <span class="annotation">@Programmatic</span> Response objectRepresentation( <i class="conum" data-value="1"></i><b>(1)</b> <span class="predefined-type">Context</span> rendererContext, ObjectAdapter objectAdapter); <span class="annotation">@Programmatic</span> Response propertyDetails( <i class="conum" data-value="2"></i><b>(2)</b> <span class="predefined-type">Context</span> rendererContext, ObjectAndProperty objectAndProperty, MemberReprMode memberReprMode); <span class="annotation">@Programmatic</span> Response collectionDetails( <i class="conum" data-value="3"></i><b>(3)</b> <span class="predefined-type">Context</span> rendererContext, ObjectAndCollection objectAndCollection, MemberReprMode memberReprMode); <span class="annotation">@Programmatic</span> Response actionPrompt( <i class="conum" data-value="4"></i><b>(4)</b> <span class="predefined-type">Context</span> rendererContext, ObjectAndAction objectAndAction); <span class="annotation">@Programmatic</span> Response actionResult( <i class="conum" data-value="5"></i><b>(5)</b> <span class="predefined-type">Context</span> rendererContext, ObjectAndActionInvocation objectAndActionInvocation, ActionResultReprRenderer.SelfLink selfLink); <span class="directive">public</span> <span class="directive">static</span> <span class="type">interface</span> <span class="class">Context</span> <span class="directive">extends</span> RendererContext { ObjectAdapterLinkTo getAdapterLinkTo(); } }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>representation of a single object, as per section 14.4 of the RO spec, v1.0</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>representation of a single property of an object, as per section 16.4 of the RO spec v1.0</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>representation of a single collection of an object, as per section 17.5 of the RO spec v1.0</td> </tr> <tr> <td><i class="conum" data-value="4"></i><b>4</b></td> <td>representation of a single action (prompt) of an object, as per section 18.2 of the RO spec v1.0</td> </tr> <tr> <td><i class="conum" data-value="5"></i><b>5</b></td> <td>representation of the results of a single action invocation, as per section 19.5 of the RO spec v1.0</td> </tr> </table> </div> <div class="paragraph"> <p>These methods provide:</p> </div> <div class="ulist"> <ul> <li> <p>a <code>RendererContext</code> which provides access to request-specific context (eg HTTP headers), session-specific context (eg authentication) and global context (eg configuration settings)</p> </li> <li> <p>an object representing the information to be rendered<br></p> <div class="paragraph"> <p>eg <code>ObjectAdapter</code>, <code>ObjectAndProperty</code>, <code>ObjectAndCollection</code> etc</p> </div> </li> <li> <p>for members, whether the representation is in read/write mode<br></p> <div class="paragraph"> <p>ie <code>MemberReprMode</code></p> </div> </li> </ul> </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>This is an "internal" SPI, meaning that it uses types that are not part of the Isis applib. We do not guarantee that <a href="http://semver.org">semantic versioning</a> will be honoured for these APIs.</p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_implementation_13">3.14.2. Implementation</h4> <div class="paragraph"> <p>As discussed in the introduction, the framework provides a default implementation, <code>o.a.i.v.ro.rendering.service.RepresentationServiceForRestfulObjects</code>. This delegates to <a href="#_rgsvc_spi_ContentNegotiationService"><code>ContentNegotiationService</code></a> to generate an alternative representation; but if none is provided then it falls back on generating the representations as defined in the <a href="http://restfulobjects.org">Restful Objects spec</a> v1.0.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_services_18">3.14.3. Registering the Services</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' default implementation of <code>RepresentationService</code> service is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> </div> <div class="sect3"> <h4 id="_related_services_20">3.14.4. Related Services</h4> <div class="paragraph"> <p>The default implementation delegates to <a href="#_rgsvc_spi_ContentNegotiationService"><code>ContentNegotiationService</code></a>, whose default implementation may delegate in turn to <a href="#_rgsvc_spi_ContentMappingService"><code>ContentMappingService</code></a> (if present).</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_spi_RoutingService">3.15. <code>RoutingService</code></h3> <div class="paragraph"> <p>The <code>RoutingService</code> provides the ability to return (and therefore render) an alternative object from an action invocation.</p> </div> <div class="paragraph"> <p>There are two primary use cases:</p> </div> <div class="ulist"> <ul> <li> <p>if an action returns an aggregate leaf (that is, a child object which has an owning parent), then the parent object can be returned instead.<br></p> <div class="paragraph"> <p>For example, an action returning <code>OrderItem</code> might instead render the owning <code>Order</code> object. It is the responsibility of the implementation to figure out what the "owning" object might be.</p> </div> </li> <li> <p>if an action returns <code>null</code> or is <code>void</code>, then return some other "useful" object.<br></p> <div class="paragraph"> <p>For example, return the home page (eg as defined by the <a href="rgant.html#_rgant_HomePage"><code>@HomePage</code></a> annotation).</p> </div> </li> </ul> </div> <div class="paragraph"> <p>Currently the routing service is used only by the <a href="ugvw.html">Wicket viewer</a>; it is ignored by the <a href="ugvro.html">Restful Objects</a> viewer.</p> </div> <div class="sect3"> <h4 id="_spi_15">3.15.1. SPI</h4> <div class="paragraph"> <p>The SPI defined by this service 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">interface</span> <span class="class">RoutingService</span> { <span class="annotation">@Programmatic</span> <span class="type">boolean</span> canRoute(<span class="predefined-type">Object</span> original); <i class="conum" data-value="1"></i><b>(1)</b> <span class="annotation">@Programmatic</span> <span class="predefined-type">Object</span> route(<span class="predefined-type">Object</span> original); <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>whether this implementation recognizes and can "route" the object. The <code>route(&#8230;&#8203;)</code> method is only called if this method returns <code>true</code>.</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>the object to use; this may be the same as the original object, some other object, or (indeed) <code>null</code>.</td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_implementation_14">3.15.2. Implementation</h4> <div class="paragraph"> <p>The framework provides a default implementation which will always return the original object provided, or the home page if a <code>null</code> or <code>void</code> was provided. It uses the <a href="#_rgsvc_api_HomePageProviderService"><code>HomePageProviderService</code></a>.</p> </div> <div class="paragraph"> <p>There can be multiple implementations of <code>RoutingService</code> registered. These are checked in turn (chain of responsibility pattern), ordered according to <a href="rgant.html#_rgant_DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip). The route from the first service that returns <code>true</code> from its <code>canRoute(&#8230;&#8203;)</code> method will be used.</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_services_19">3.15.3. Registering the Services</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' default implementation of <code>RoutingService</code> service is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> </div> <div class="sect3"> <h4 id="_related_services_21">3.15.4. Related Services</h4> <div class="paragraph"> <p>The default implementation of ths service uses the <a href="#_rgsvc_api_HomePageProviderService"><code>HomePageProviderService</code></a>.</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_spi_TranslationService">3.16. <code>TranslationService</code></h3> <div class="paragraph"> <p>The <code>TranslationService</code> is the cornerstone of Apache Isis' i18n support. Its role is to be able to provide translated versions of the various elements within the Apache Isis metamodel (service and object classes, properties, collections, actions, action parameters) and also to translate business rule (disable/valid) messages, and exceptions. These translations provide for both singular and plural forms.</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>For the "big picture" and further details on Apache Isis' i18n support, see <a href="ugbtb.html#_ugbtb_i18n">here</a>.</p> </div> </td> </tr> </table> </div> <div class="sect3"> <h4 id="_spi_16">3.16.1. SPI</h4> <div class="paragraph"> <p>The SPI defined by this service 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">interface</span> <span class="class">TranslationService</span> { <span class="annotation">@Programmatic</span> <span class="predefined-type">String</span> translate(<span class="predefined-type">String</span> context, <span class="predefined-type">String</span> text); <i class="conum" data-value="1"></i><b>(1)</b> <span class="annotation">@Programmatic</span> <span class="predefined-type">String</span> translate(<span class="predefined-type">String</span> context, <i class="conum" data-value="2"></i><b>(2)</b> <span class="predefined-type">String</span> singularText, <span class="predefined-type">String</span> pluralText, <span class="type">int</span> num); <span class="type">enum</span> Mode { READ, WRITE;} <span class="annotation">@Programmatic</span> 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>translate the text, in the locale of the "current user".</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>return a translation of either the singular or the plural text, dependent on the <code>num</code> parameter, in the locale of the "current user"</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>whether this implementation is operating in read or in write mode.</td> </tr> </table> </div> <div class="paragraph"> <p>If in read mode, then the translations are expected to be present.</p> </div> <div class="paragraph"> <p>If in write mode, then the implementation is saving translation keys, and will always return the untranslated translation.</p> </div> </div> <div class="sect3"> <h4 id="_implementation_15">3.16.2. Implementation</h4> <div class="paragraph"> <p>The Apache Isis framework provides a default implementation (<code>TranslationServicePo</code>) that uses the GNU <code>.pot</code> and <code>.po</code> files for translations. It relies on the <code>LocaleProvider</code> service (to return the <code>Locale</code> of the current user) and also the <code>TranslationsResolver</code> service (to read existing translations).</p> </div> <div class="paragraph"> <p>The framework also provides a supporting <code>TranslationServicePoMenu</code> provides menu items under the "Prototyping" secondary menu for controlling this service and downloading <code>.pot</code> files for translation.</p> </div> <div class="paragraph"> <p>For more details on the implementation, see <a href="ugbtb.html#_ugbtb_i18n">i18n support</a>.</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_services_20">3.16.3. Registering the Services</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#_rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>) then Apache Isis' core implementation of <code>TranslationService</code> service (along with the supporting menu service) are automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>If the menu items are not required then these can be suppressed either using security or by implementing a <a href="ugbtb.html#_ugbtb_decoupling_vetoing-visibility">vetoing subscriber</a>.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> <div class="sect3"> <h4 id="_related_services_22">3.16.4. Related Services</h4> <div class="paragraph"> <p>This service works in conjunction with <a href="#_rgsvc_spi_LocaleProvider"><code>LocaleProvider</code></a> and <a href="#_rgsvc_spi_TranslationsResolver"><code>TranslationsResolver</code></a> in order to provide i18n support.</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_spi_TranslationsResolver">3.17. <code>TranslationsResolver</code></h3> <div class="paragraph"> <p>The <code>TranslationsResolver</code> service is one of the services that work together to implement Apache Isis' support for i18n, being used by Isis' default implementation of <a href="#_rgsvc_spi_TranslationService"><code>TranslationService</code></a>.</p> </div> <div class="paragraph"> <p>The role of the service itself is locate and return translations.</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>For the "big picture" and further details on Apache Isis' i18n support, see <a href="ugbtb.html#_ugbtb_i18n">here</a>.</p> </div> </td> </tr> </table> </div> <div class="sect3"> <h4 id="_spi_17">3.17.1. SPI</h4> <div class="paragraph"> <p>The SPI defined by this service 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">interface</span> <span class="class">TranslationsResolver</span> { <span class="annotation">@Programmatic</span> <span class="predefined-type">List</span>&lt;<span class="predefined-type">String</span>&gt; readLines(<span class="directive">final</span> <span class="predefined-type">String</span> file); }</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_implementation_16">3.17.2. Implementation</h4> <div class="paragraph"> <p>Isis' <a href="ugvw.html">Wicket viewer</a> provides an implementation of this service (<code>TranslationsResolverWicket</code>) which leverages Apache Wicket APIs. This searches for translation files in the standard <code>WEB-INF/</code> directory.</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>Currently there is no equivalent implementation for the <a href="ugvro.html">RestfulObjects viewer</a>.</p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_registering_the_service_17">3.17.3. Registering the Service</h4> <div class="paragraph"> <p>Assuming that the <code>configuration-and-annotation</code> services installer is configured (implicit if using the <code>AppManifest</code> to <a href="rgcms.html#<em>rgcms_classes_AppManifest-bootstrapping">bootstrap the app</a>), _and</em> that the <a href="ugvw.html">Wicket viewer</a> is being used, then an implementation of <code>TranslationsResolver</code> is automatically registered and injected (it is annotated with <code>@DomainService</code>) so no further configuration is required.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> <div class="sect3"> <h4 id="_related_services_23">3.17.4. Related Services</h4> <div class="paragraph"> <p>This service works in conjunction with <a href="#_rgsvc_spi_LocaleProvider"><code>LocaleProvider</code></a> and <a href="#_rgsvc_spi_TranslationService"><code>TranslationService</code></a> in order to provide i18n support.</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_spi_UrlEncodingService">3.18. <code>UrlEncodingService</code></h3> <div class="paragraph"> <p>The <code>UrlEncodingService</code> defines a consistent way to convert strings to/from a form safe for use within a URL. The service is used by the framework to map <a href="ugbtb.html#_ugbtb_view-models">view model</a> mementos (derived from the state of the view model itself) into a form that can be used as a view model. When the framework needs to recreate the view model (for example to invoke an action on it), this URL is converted back into a view model memento, from which the view model can then be hydrated.</p> </div> <div class="paragraph"> <p>Defining this functionality as an SPI has two use cases:</p> </div> <div class="ulist"> <ul> <li> <p>first, (though some browsers support longer strings), there is a limit of 2083 characters for URLs. For view model mementos that correspond to large strings (as might occur when serializing a JAXB <a href="rgant.html#_rgant-XmlRootElement"><code>@XmlRootElement</code></a>-annotated view model), the service provides a hook. <br></p> <div class="paragraph"> <p>For example, each memento string could be mapped to a GUID held in some cluster-aware cache.</p> </div> </li> <li> <p>the service provides the ability, to encrypt the string in order to avoid leakage of potentially sensitive state within the URL.</p> </li> </ul> </div> <div class="paragraph"> <p>The framework provides a default implementation of this service, <code>UrlEncodingServiceUsingBaseEncoding</code> (also in the applib) that uses <code>base-64</code> encoding to <code>UTF-8</code> charset.</p> </div> <div class="sect3"> <h4 id="_spi_18">3.18.1. SPI</h4> <div class="paragraph"> <p>The SPI defined by the service 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">interface</span> <span class="class">UrlEncodingService</span> { <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="predefined-type">String</span> encode(<span class="directive">final</span> <span class="predefined-type">String</span> str); <i class="conum" data-value="1"></i><b>(1)</b> <span class="annotation">@Programmatic</span> <span class="directive">public</span> <span class="predefined-type">String</span> decode(<span class="predefined-type">String</span> str); <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>convert the string (eg view model memento) into a string safe for use within an URL</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>unconvert the string from its URL form into its original form URL</td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_implementation_17">3.18.2. Implementation</h4> <div class="paragraph"> <p>The framework provides a default implementation (<code>UrlEncodingServiceUsingBaseEncoding</code>) that simply converts the string using base-64 encoding and UTF-8 character set. As already noted, be aware that the maximum length of a URL should not exceed 2083 characters. For large view models, there&#8217;s the possibility that this limit could be exceeded; in such cases register an alternative implementation of this service.</p> </div> <div class="paragraph"> <p>To use an alternative implementation, use <a href="rgant.html#_rgant-DomainServiceLayout_menuOrder"><code>@DomainServiceLayout#menuOrder()</code></a> (as explained further in this <a href="ugfun.html#_ugfun_how-tos_replacing-default-service-implementations">"how to"</a> tip).</p> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_spi_UserProfileService">3.19. <code>UserProfileService</code></h3> <div class="paragraph"> <p>The <code>UserProfileService</code> provides the ability for the domain application to return supplementary metadata about the current user. This information is used (by the <a href="ugvw.html">Wicket viewer</a>) to customize the appearance of the tertiary "Me" menu bar (top right). For example, rather than display the username, instead the user&#8217;s first and last name could be displayed.</p> </div> <div class="paragraph"> <p>Another use case is to allow the user to switch context in some fashion or other. This might be to emulate a sort of "sudo"-like function, or perhaps to focus on some particular set of data.</p> </div> <div class="sect3"> <h4 id="_spi_19">3.19.1. SPI</h4> <div class="paragraph"> <p>The SPI defined by the service 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">interface</span> <span class="class">UserProfileService</span> { <span class="annotation">@Programmatic</span> <span class="predefined-type">String</span> userProfileName(); <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>is used (in the Wicket viewer) as the menu name of the tertiary "Me" menu bar.</td> </tr> </table> </div> <div class="paragraph"> <p>If the method returns <code>null</code> or throws an exception then the framework will default to using the current user name.</p> </div> <div class="paragraph"> <p>In the future this API may be expanded; one obvious possibility is to return a profile photo or avatar URL.</p> </div> </div> <div class="sect3"> <h4 id="_implementation_18">3.19.2. Implementation</h4> <div class="paragraph"> <p>There is no default implementation of this service provided by the core Apache Isis framework.</p> </div> <div class="paragraph"> <p>An example implementation can be found in the (non-ASF) <a href="http://github.com/isisaddons/isis-app-todoapp">Isis addons' todoapp</a>:</p> </div> <div class="imageblock"> <div class="content"> <a class="image" href="images/reference-services-spi/UserProfileService/todoapp.png"><img src="images/reference-services-spi/UserProfileService/todoapp.png" alt="todoapp" width="800px"></a> </div> </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>Currently this feature is not integrated with Apache Isis' authentication mechanisms; the information provided is purely metadata provided for presentation purposes only.</p> </div> </td> </tr> </table> </div> </div> </div> <div class="sect2"> <h3 id="_rgsvc_spi_UserRegistrationService">3.20. <code>UserRegistrationService</code></h3> <div class="paragraph"> <p>The <code>UserRegistrationService</code> provides the ability for users to sign-up to access an application by providing a valid email address, and also provides the capability for users to reset their password if forgotten.</p> </div> <div class="paragraph"> <p>For user sign-up, the <a href="ugvw.html">Wicket viewer</a> will check whether an implementation of this service (and also the <a href="#<em>rgsvc_spi_EmailNotificationService"><code>EmailNotificationService</code></a>) is available, and if so will render a sign-up page where the user enters their email address. A verification email is sent (using the aforementioned <code>EmailNotificationService</code>) which includes a link back to the running application; this allows the user then to complete their registration process (choose user name, password and so on). When the user has provided the additional details, the Wicket viewer calls _this</em> service in order to create an account for them, and then logs the user on.</p> </div> <div class="paragraph"> <p>For the password reset feature, the Wicket viewer will render a password reset page, and use the <code>EmailNotificationService</code> to send a "password forgotten" email. This service provides the ability to reset a password based on the user&#8217;s email address.</p> </div> <div class="paragraph"> <p>It is of course possible for domain objects to use this service; it will be injected into domain object or other domain services in the usual way. That said, we expect that such use cases will be comparatively rare; the primary use case is for the Wicket viewer&#8217;s sign-up page.</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>For further details on the user registration feature (as supported by the Wicket viewer), see <a href="ugvw.html#_ugvw_features_user-registration">here</a>.</p> </div> </td> </tr> </table> </div> <div class="sect3"> <h4 id="_spi_20">3.20.1. SPI</h4> <div class="paragraph"> <p>The SPI defined by the service 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">interface</span> <span class="class">UserRegistrationService</span> { <span class="annotation">@Programmatic</span> <span class="type">boolean</span> usernameExists(<span class="predefined-type">String</span> username); <i class="conum" data-value="1"></i><b>(1)</b> <span class="annotation">@Programmatic</span> <span class="type">boolean</span> emailExists(<span class="predefined-type">String</span> emailAddress); <i class="conum" data-value="2"></i><b>(2)</b> <span class="annotation">@Programmatic</span> <span class="type">void</span> registerUser(<span class="predefined-type">String</span> username, <span class="predefined-type">String</span> password, <span class="predefined-type">String</span> emailAddress); <i class="conum" data-value="3"></i><b>(3)</b> <span class="annotation">@Programmatic</span> <span class="type">boolean</span> updatePasswordByEmail(<span class="predefined-type">String</span> emailAddress, <span class="predefined-type">String</span> password); <i class="conum" data-value="4"></i><b>(4)</b> }</code></pre> </div> </div> <div class="colist arabic"> <table> <tr> <td><i class="conum" data-value="1"></i><b>1</b></td> <td>checks if there is already a user with the specified username</td> </tr> <tr> <td><i class="conum" data-value="2"></i><b>2</b></td> <td>checks if there is already a user with the specified email address</td> </tr> <tr> <td><i class="conum" data-value="3"></i><b>3</b></td> <td>creates the user, with specified password and email address. The username and email address must both be unique (not being used by an existing user)</td> </tr> <tr> <td><i class="conum" data-value="4"></i><b>4</b></td> <td>allows the user to reset their password</td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_implementation_19">3.20.2. Implementation</h4> <div class="paragraph"> <p>The core Apache Isis framework itself defines only an API; there is no default implementation. Rather, the implementation will depend on the security mechanism being used.</p> </div> <div class="paragraph"> <p>That said, if you have configured your app to use the <a href="http://github.com/isisaddons/isis-module-security">Isis addons security module</a>, then note that the security module does provide an abstract implementation (<code>SecurityModuleAppUserRegistrationServiceAbstract</code>) of the <code>UserRegistrationService</code>. You will need to extend that service and provide implementation for the two abstract methods: <code>getInitialRole()</code> and <code>getAdditionalInitialRoles()</code>.</p> </div> <div class="paragraph"> <p>This is needed so that the self-registered users are assigned automatically to your application role(s) and be able to use the application. Without any role such user will be able only to see/use the logout link of the application.</p> </div> </div> <div class="sect3"> <h4 id="_registering_the_services_21">3.20.3. Registering the Services</h4> <div class="paragraph"> <p>There is no default implementation of this service provided by the core Apache Isis framework.</p> </div> <div class="paragraph"> <p>If using the (non-ASF) <a href="http://github.com/isisaddons/isis-module-security">Isis addons' security</a> module) for authentication and authorization, then note that it provides an adapter class, <code>SecurityModuleAppUserRegistrationServiceAbstract</code>, that provides most of the implementation. You are still required to implement a subclass and register.</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">@DomainService</span>(nature=NatureOfService.DOMAIN) <span class="directive">public</span> <span class="type">class</span> <span class="class">AppUserRegistrationService</span> <span class="directive">extends</span> SecurityModuleAppUserRegistrationServiceAbstract { <span class="directive">protected</span> ApplicationRole getInitialRole() { <span class="keyword">return</span> findRole(<span class="string"><span class="delimiter">&quot;</span><span class="content">regular-user</span><span class="delimiter">&quot;</span></span>); } <span class="directive">protected</span> <span class="predefined-type">Set</span>&lt;ApplicationRole&gt; getAdditionalInitialRoles() { <span class="keyword">return</span> <span class="predefined-type">Collections</span>.singleton(findRole(<span class="string"><span class="delimiter">&quot;</span><span class="content">self-registered-user</span><span class="delimiter">&quot;</span></span>)); } <span class="directive">private</span> ApplicationRole findRole(<span class="directive">final</span> <span class="predefined-type">String</span> roleName) { <span class="keyword">return</span> applicationRoles.findRoleByName(roleName); } <span class="annotation">@Inject</span> <span class="directive">private</span> ApplicationRoles applicationRoles; }</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_related_services_24">3.20.4. Related Services</h4> <div class="paragraph"> <p>The most common use case is to allow users to sign-up through Apache Isis' Wicket viewer. Because the process requires email to be sent, the following services must be configured:</p> </div> <div class="ulist"> <ul> <li> <p><a href="#_rgsvc_api_EmailService"><code>EmailService</code></a></p> </li> <li> <p><a href="#_rgsvc_spi_EmailNotificationService"><code>EmailNotificationService</code></a></p> </li> <li> <p><code>UserRegistrationService</code> (this service)</p> </li> </ul> </div> <div class="paragraph"> <p>The <code>EmailService</code> in particular requires additional <a href="rgcfg.html#_rgcfg_configuring-core">configuration properties</a> to specify the external SMTP service.</p> </div> </div> </div> </div> </div> </div> <footer> <hr> <p class="small"> Copyright &copy; 2010~2015 The Apache&nbsp;Software&nbsp;Foundation, licensed under the Apache&nbsp;License,&nbsp;v2.0. <br/> Apache, the Apache feather logo, Apache&nbsp;Isis, and the Apache&nbsp;Isis project logo are all trademarks of The&nbsp;Apache&nbsp;Software&nbsp;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="#_rgsvc">1. Domain Services</a> <ul class="sectlevel2"> <li><a href="#_other_guides">1.1. Other Guides</a></li> </ul> </li> <li><a href="#_rgsvc_api">2. Domain Services API</a> <ul class="sectlevel2"> <li><a href="#_rgsvc_api_AcceptHeaderService">2.1. <code>AcceptHeaderService</code></a> <ul class="sectlevel3"> <li><a href="#_api_implementation">2.1.1. API &amp; Implementation</a></li> <li><a href="#_usage">2.1.2. Usage</a></li> <li><a href="#_registering_the_service">2.1.3. Registering the Service</a></li> </ul> </li> <li><a href="#_rgsvc_api_ActionInvocationContext">2.2. <code>ActionInvocationContext</code></a> <ul class="sectlevel3"> <li><a href="#_api">2.2.1. API</a></li> <li><a href="#_usage_2">2.2.2. Usage</a></li> <li><a href="#_registering_the_service_2">2.2.3. Registering the Service</a></li> <li><a href="#_unit_testing_support">2.2.4. Unit testing support</a></li> </ul> </li> <li><a href="#_rgsvc_api_BackgroundService">2.3. <code>BackgroundService</code></a> <ul class="sectlevel3"> <li><a href="#_api_implementation_2">2.3.1. API &amp; Implementation</a></li> <li><a href="#_usage_3">2.3.2. Usage</a></li> <li><a href="#_end_user_experience">2.3.3. End-user experience</a></li> <li><a href="#_registering_the_services">2.3.4. Registering the Services</a></li> <li><a href="#_related_services">2.3.5. Related Services</a></li> <li><a href="#_rgsvc_api_BackgroundService_BackgroundCommandExecution">2.3.6. <code>BackgroundCommandExecution</code> abstract class</a></li> <li><a href="#_rgsvc_api_BackgroundService_Quartz">2.3.7. Quartz Scheduler Configuration</a></li> </ul> </li> <li><a href="#_rgsvc_api_BookmarkService">2.4. <code>BookmarkService</code></a> <ul class="sectlevel3"> <li><a href="#_api_implementation_3">2.4.1. API &amp; Implementation</a></li> <li><a href="#__code_bookmarkholder_code">2.4.2. <code>BookmarkHolder</code></a></li> <li><a href="#_usage_by_other_services">2.4.3. Usage by other services</a></li> <li><a href="#_registering_the_service_3">2.4.4. Registering the Service</a></li> </ul> </li> <li><a href="#_rgsvc_api_ClockService">2.5. <code>ClockService</code></a> <ul class="sectlevel3"> <li><a href="#_api_implementation_4">2.5.1. API &amp; Implementation</a></li> <li><a href="#_testing_support">2.5.2. Testing Support</a> <ul class="sectlevel4"> <li><a href="#_alternative_implementations">Alternative Implementations</a></li> </ul> </li> <li><a href="#_registering_the_service_4">2.5.3. Registering the Service</a></li> </ul> </li> <li><a href="#_rgsvc_api_CommandContext">2.6. <code>CommandContext</code></a> <ul class="sectlevel3"> <li><a href="#_rgsvc_api_CommandContext_screencast">2.6.1. Screencast</a></li> <li><a href="#_api_implementation_5">2.6.2. API &amp; Implementation</a></li> <li><a href="#_usage_4">2.6.3. Usage</a></li> <li><a href="#_interacting_with_the_services">2.6.4. Interacting with the services</a></li> <li><a href="#_registering_the_services_2">2.6.5. Registering the Services</a></li> <li><a href="#_related_services_2">2.6.6. Related Services</a></li> </ul> </li> <li><a href="#_rgsvc_api_DeepLinkService">2.7. <code>DeepLinkService</code></a> <ul class="sectlevel3"> <li><a href="#_api_implementation_6">2.7.1. API &amp; Implementation</a></li> <li><a href="#_usage_within_the_framework">2.7.2. Usage within the framework</a></li> <li><a href="#_implementations">2.7.3. Implementations</a></li> <li><a href="#_registering_the_services_3">2.7.4. Registering the Services</a></li> </ul> </li> <li><a href="#_rgsvc_api_DomainObjectContainer">2.8. <code>DomainObjectContainer</code></a> <ul class="sectlevel3"> <li><a href="#_rgsvc_api_DomainObjectContainer_object-creation-api">2.8.1. Object Creation API</a></li> <li><a href="#_rgsvc_api_DomainObjectContainer_generic-repository-api">2.8.2. Generic Repository API</a></li> <li><a href="#_rgsvc_api_DomainObjectContainer_object-persistence-api">2.8.3. Object Persistence API</a></li> <li><a href="#_rgsvc_api_DomainObjectContainer_messages-api">2.8.4. Messages API</a></li> <li><a href="#_rgsvc_api_DomainObjectContainer_security-api">2.8.5. Security API</a></li> <li><a href="#_rgsvc_api_DomainObjectContainer_presentation-api">2.8.6. Presentation API</a></li> <li><a href="#_rgsvc_api_DomainObjectContainer_validation-api">2.8.7. Validation API</a></li> <li><a href="#_rgsvc_api_DomainObjectContainer_properties-api">2.8.8. Properties API</a></li> <li><a href="#_rgsvc_api_DomainObjectContainer_services-api">2.8.9. Services API</a></li> <li><a href="#_registering_the_service_5">2.8.10. Registering the Service</a></li> </ul> </li> <li><a href="#_rgsvc_api_EmailService">2.9. <code>EmailService</code></a> <ul class="sectlevel3"> <li><a href="#_api_implementation_7">2.9.1. API &amp; Implementation</a></li> <li><a href="#_configuration">2.9.2. Configuration</a></li> <li><a href="#_alternative_implementations_2">2.9.3. Alternative Implementations</a></li> <li><a href="#_registering_the_services_4">2.9.4. Registering the Services</a></li> <li><a href="#_related_services_3">2.9.5. Related Services</a></li> </ul> </li> <li><a href="#_rgsvc_api_EventBusService">2.10. <code>EventBusService</code></a> <ul class="sectlevel3"> <li><a href="#_rgsvc_api_EventBusService_api-and-implementation">2.10.1. API &amp; Implementation</a></li> <li><a href="#_registering_subscribers">2.10.2. Registering Subscribers</a></li> <li><a href="#_annotating_members">2.10.3. Annotating Members</a></li> <li><a href="#_rgsvc_api_EventBusService_event-hierarchy">2.10.4. Event hierarchy</a> <ul class="sectlevel4"> <li><a href="#_variation_for_contributing_services">Variation (for contributing services)</a></li> </ul> </li> <li><a href="#_programmatic_posting">2.10.5. Programmatic posting</a></li> <li><a href="#_using_code_wrapperfactory_code">2.10.6. Using <code>WrapperFactory</code></a></li> <li><a href="#_implementation_spi">2.10.7. Implementation SPI</a></li> <li><a href="#_rgsvc_api_EventBusService_Configuration">2.10.8. Configuration</a></li> <li><a href="#_registering_the_services_5">2.10.9. Registering the Services</a></li> <li><a href="#_related_services_4">2.10.10. Related Services</a></li> </ul> </li> <li><a href="#_rgsvc_api_FixtureScriptsDefault">2.11. <code>FixtureScriptsDefault</code></a> <ul class="sectlevel3"> <li><a href="#_api_implementation_8">2.11.1. API &amp; Implementation</a></li> <li><a href="#_configuration_2">2.11.2. Configuration</a></li> <li><a href="#_related_services_5">2.11.3. Related Services</a></li> </ul> </li> <li><a href="#_rgsvc_api_GuiceBeanProvider">2.12. <code>GuiceBeanProvider</code></a> <ul class="sectlevel3"> <li><a href="#_api_implementation_9">2.12.1. API &amp; Implementation</a></li> <li><a href="#_usage_5">2.12.2. Usage</a></li> <li><a href="#_registering_the_services_6">2.12.3. Registering the Services</a></li> </ul> </li> <li><a href="#_rgsvc_api_HomePageProviderService">2.13. <code>HomePageProviderService</code></a> <ul class="sectlevel3"> <li><a href="#_api_implementation_10">2.13.1. API &amp; Implementation</a></li> <li><a href="#_registering_the_service_6">2.13.2. Registering the Service</a></li> </ul> </li> <li><a href="#_rgsvc_api_IsisJdoSupport">2.14. <code>IsisJdoSupport</code></a> <ul class="sectlevel3"> <li><a href="#_rgsvc_api_IsisJdoSupport_executing-sql">2.14.1. Executing SQL</a></li> <li><a href="#_rgsvc_api_IsisJdoSupport_type-safe-jdoql-queries">2.14.2. Type-safe JDOQL Queries</a></li> <li><a href="#_rgsvc_api_IsisJdoSupport_fixture-support">2.14.3. Fixture support</a></li> <li><a href="#_rgsvc_api_IsisJdoSupport_reloading-entities">2.14.4. Reloading entities</a></li> <li><a href="#_rgsvc_api_IsisJdoSupport_jdo-persistencemanager">2.14.5. JDO <code>PersistenceManager</code></a></li> <li><a href="#_registering_the_services_7">2.14.6. Registering the Services</a></li> </ul> </li> <li><a href="#_rgsvc_api_JaxbService">2.15. <code>JaxbService</code></a> <ul class="sectlevel3"> <li><a href="#_rgsvc_api_JaxbService_api-and-implementation">2.15.1. API &amp; Implementation</a></li> <li><a href="#_usage_within_the_framework_2">2.15.2. Usage within the framework</a></li> <li><a href="#_registering_the_service_7">2.15.3. Registering the Service</a></li> </ul> </li> <li><a href="#_rgsvc_api_MementoService">2.16. <code>MementoService</code></a> <ul class="sectlevel3"> <li><a href="#_api_implementation_11">2.16.1. API &amp; Implementation</a></li> <li><a href="#_usage_6">2.16.2. Usage</a></li> <li><a href="#_related_services_6">2.16.3. Related Services</a></li> <li><a href="#_registering_the_service_8">2.16.4. Registering the Service</a></li> </ul> </li> <li><a href="#_rgsvc_api_MetamodelService">2.17. <code>MetamodelService</code></a> <ul class="sectlevel3"> <li><a href="#_api_implementation_12">2.17.1. API &amp; Implementation</a></li> <li><a href="#_registering_the_service_9">2.17.2. Registering the Service</a></li> </ul> </li> <li><a href="#_rgsvc_api_QueryResultsCache">2.18. <code>QueryResultsCache</code></a> <ul class="sectlevel3"> <li><a href="#_api_implementation_13">2.18.1. API &amp; Implementation</a></li> <li><a href="#_usage_7">2.18.2. Usage</a></li> <li><a href="#_registering_the_service_10">2.18.3. Registering the Service</a></li> <li><a href="#_related_services_7">2.18.4. Related Services</a></li> </ul> </li> <li><a href="#_rgsvc_api_Scratchpad">2.19. <code>Scratchpad</code></a> <ul class="sectlevel3"> <li><a href="#_api_implementation_14">2.19.1. API &amp; Implementation</a></li> <li><a href="#_usage_8">2.19.2. Usage</a></li> <li><a href="#_registering_the_service_11">2.19.3. Registering the Service</a></li> <li><a href="#_related_services_8">2.19.4. Related Services</a></li> </ul> </li> <li><a href="#_rgsvc_api_SudoService">2.20. <code>SudoService</code></a> <ul class="sectlevel3"> <li><a href="#_api_implementation_15">2.20.1. API &amp; Implementation</a></li> <li><a href="#_usage_9">2.20.2. Usage</a></li> <li><a href="#_registering_the_service_12">2.20.3. Registering the Service</a></li> </ul> </li> <li><a href="#_rgsvc_api_SwaggerService">2.21. <code>SwaggerService</code></a> <ul class="sectlevel3"> <li><a href="#_rgsvc_api_SwaggerService_api-and-implementation">2.21.1. API &amp; Implementation</a></li> <li><a href="#_usage_within_the_framework_3">2.21.2. Usage within the framework</a></li> <li><a href="#_registering_the_service_13">2.21.3. Registering the Service</a></li> <li><a href="#_related_services_9">2.21.4. Related Services</a></li> </ul> </li> <li><a href="#_rgsvc_api_WrapperFactory">2.22. <code>WrapperFactory</code></a> <ul class="sectlevel3"> <li><a href="#_api_2">2.22.1. API</a></li> <li><a href="#_usage_10">2.22.2. Usage</a></li> <li><a href="#_listener_api">2.22.3. Listener API</a></li> <li><a href="#_registering_the_service_14">2.22.4. Registering the Service</a></li> </ul> </li> <li><a href="#_rgsvc_api_XmlSnapshotService">2.23. <code>XmlSnapshotService</code></a> <ul class="sectlevel3"> <li><a href="#_standard_api">2.23.1. Standard API</a></li> <li><a href="#_builder_api">2.23.2. Builder API</a></li> <li><a href="#_automatic_inclusions">2.23.3. Automatic inclusions</a></li> <li><a href="#_convenience_api">2.23.4. Convenience API</a></li> <li><a href="#_registering_the_service_15">2.23.5. Registering the Service</a></li> <li><a href="#_related_services_10">2.23.6. Related Services</a></li> </ul> </li> </ul> </li> <li><a href="#_rgsvc_spi">3. Domain Services SPI</a> <ul class="sectlevel2"> <li><a href="#_rgsvc_spi_AuditingService">3.1. <code>AuditingService3</code></a> <ul class="sectlevel3"> <li><a href="#_spi">3.1.1. SPI</a></li> <li><a href="#_implementation">3.1.2. Implementation</a></li> <li><a href="#_usage_11">3.1.3. Usage</a></li> <li><a href="#_registering_the_services_8">3.1.4. Registering the Services</a></li> <li><a href="#_related_services_11">3.1.5. Related Services</a></li> </ul> </li> <li><a href="#_rgsvc_spi_BackgroundCommandService">3.2. <code>BackgroundCommandService</code></a> <ul class="sectlevel3"> <li><a href="#_spi_2">3.2.1. SPI</a></li> <li><a href="#__internal_spi">3.2.2. "Internal" SPI</a></li> <li><a href="#_implementation_2">3.2.3. Implementation</a></li> <li><a href="#_usage_12">3.2.4. Usage</a></li> <li><a href="#_registering_the_services_9">3.2.5. Registering the Services</a></li> <li><a href="#_related_services_12">3.2.6. Related Services</a></li> </ul> </li> <li><a href="#_rgsvc_spi_ClassDiscoveryService">3.3. <code>ClassDiscoveryService</code></a> <ul class="sectlevel3"> <li><a href="#_spi_3">3.3.1. SPI</a></li> <li><a href="#_implementation_3">3.3.2. Implementation</a></li> <li><a href="#_usage_13">3.3.3. Usage</a></li> <li><a href="#_registering_the_services_10">3.3.4. Registering the Services</a></li> </ul> </li> <li><a href="#_rgsvc_spi_CommandService">3.4. <code>CommandService</code></a> <ul class="sectlevel3"> <li><a href="#_spi_4">3.4.1. SPI</a></li> <li><a href="#_implementation_4">3.4.2. Implementation</a></li> <li><a href="#_usage_14">3.4.3. Usage</a></li> <li><a href="#_registering_the_services_11">3.4.4. Registering the Services</a></li> <li><a href="#_related_services_13">3.4.5. Related Services</a></li> </ul> </li> <li><a href="#_rgsvc_spi_ContentMappingService">3.5. <code>ContentMappingService</code></a> <ul class="sectlevel3"> <li><a href="#_spi_5">3.5.1. SPI</a></li> <li><a href="#_implementations_2">3.5.2. Implementations</a></li> <li><a href="#_related_services_14">3.5.3. Related Services</a></li> </ul> </li> <li><a href="#_rgsvc_spi_ContentNegotiationService">3.6. <code>ContentNegotiationService</code></a> <ul class="sectlevel3"> <li><a href="#_spi_6">3.6.1. SPI</a></li> <li><a href="#_implementation_5">3.6.2. Implementation</a></li> <li><a href="#_usage_15">3.6.3. Usage</a></li> <li><a href="#_configuration_3">3.6.4. Configuration</a></li> <li><a href="#_registering_the_services_12">3.6.5. Registering the Services</a></li> <li><a href="#_related_services_15">3.6.6. Related Services</a></li> </ul> </li> <li><a href="#_rgsvc_spi_EmailNotificationService">3.7. <code>EmailNotificationService</code></a> <ul class="sectlevel3"> <li><a href="#_spi_7">3.7.1. SPI</a></li> <li><a href="#_implementation_6">3.7.2. Implementation</a> <ul class="sectlevel4"> <li><a href="#_alternative_implementations_3">Alternative Implementations</a></li> </ul> </li> <li><a href="#_registering_the_service_16">3.7.3. Registering the Service</a></li> <li><a href="#_related_services_16">3.7.4. Related Services</a></li> </ul> </li> <li><a href="#_rgsvc_spi_ErrorReportingService">3.8. <code>ErrorReportingService</code></a> <ul class="sectlevel3"> <li><a href="#_spi_8">3.8.1. SPI</a></li> <li><a href="#_implementation_7">3.8.2. Implementation</a></li> <li><a href="#_registering_the_services_13">3.8.3. Registering the Services</a></li> </ul> </li> <li><a href="#_rgsvc_spi_EventSerializer">3.9. <code>EventSerializer</code></a> <ul class="sectlevel3"> <li><a href="#_spi_9">3.9.1. SPI</a></li> <li><a href="#_implementation_8">3.9.2. Implementation</a></li> <li><a href="#_registering_the_services_14">3.9.3. Registering the Services</a></li> <li><a href="#_related_services_17">3.9.4. Related Services</a></li> </ul> </li> <li><a href="#_rgsvc_spi_ExceptionRecognizer">3.10. <code>ExceptionRecognizer</code></a> <ul class="sectlevel3"> <li><a href="#_spi_10">3.10.1. SPI</a></li> <li><a href="#_implementation_9">3.10.2. Implementation</a></li> <li><a href="#_registering_the_services_15">3.10.3. Registering the Services</a></li> </ul> </li> <li><a href="#_rgsvc_spi_FixtureScriptsSpecificationProvider">3.11. <code>FixtureScriptsSpecificationProvider</code></a> <ul class="sectlevel3"> <li><a href="#_spi_11">3.11.1. SPI</a></li> <li><a href="#_implementation_10">3.11.2. Implementation</a></li> </ul> </li> <li><a href="#_rgsvc_spi_LocaleProvider">3.12. <code>LocaleProvider</code></a> <ul class="sectlevel3"> <li><a href="#_spi_12">3.12.1. SPI</a></li> <li><a href="#_implementation_11">3.12.2. Implementation</a></li> <li><a href="#_registering_the_services_16">3.12.3. Registering the Services</a></li> <li><a href="#_related_services_18">3.12.4. Related Services</a></li> </ul> </li> <li><a href="#_rgsvc_spi_PublishingService">3.13. <code>PublishingService</code></a> <ul class="sectlevel3"> <li><a href="#_spi_13">3.13.1. SPI</a></li> <li><a href="#_implementation_12">3.13.2. Implementation</a> <ul class="sectlevel4"> <li><a href="#_isis_module_publishing">isis-module-publishing</a></li> <li><a href="#_isis_module_publishmq">isis-module-publishmq</a></li> </ul> </li> <li><a href="#_usage_16">3.13.3. Usage</a></li> <li><a href="#_registering_the_services_17">3.13.4. Registering the Services</a></li> <li><a href="#_related_services_19">3.13.5. Related Services</a></li> <li><a href="#_design_notes">3.13.6. Design Notes</a></li> </ul> </li> <li><a href="#_rgsvc_spi_RepresentationService">3.14. <code>RepresentationService</code></a> <ul class="sectlevel3"> <li><a href="#_spi_14">3.14.1. SPI</a></li> <li><a href="#_implementation_13">3.14.2. Implementation</a></li> <li><a href="#_registering_the_services_18">3.14.3. Registering the Services</a></li> <li><a href="#_related_services_20">3.14.4. Related Services</a></li> </ul> </li> <li><a href="#_rgsvc_spi_RoutingService">3.15. <code>RoutingService</code></a> <ul class="sectlevel3"> <li><a href="#_spi_15">3.15.1. SPI</a></li> <li><a href="#_implementation_14">3.15.2. Implementation</a></li> <li><a href="#_registering_the_services_19">3.15.3. Registering the Services</a></li> <li><a href="#_related_services_21">3.15.4. Related Services</a></li> </ul> </li> <li><a href="#_rgsvc_spi_TranslationService">3.16. <code>TranslationService</code></a> <ul class="sectlevel3"> <li><a href="#_spi_16">3.16.1. SPI</a></li> <li><a href="#_implementation_15">3.16.2. Implementation</a></li> <li><a href="#_registering_the_services_20">3.16.3. Registering the Services</a></li> <li><a href="#_related_services_22">3.16.4. Related Services</a></li> </ul> </li> <li><a href="#_rgsvc_spi_TranslationsResolver">3.17. <code>TranslationsResolver</code></a> <ul class="sectlevel3"> <li><a href="#_spi_17">3.17.1. SPI</a></li> <li><a href="#_implementation_16">3.17.2. Implementation</a></li> <li><a href="#_registering_the_service_17">3.17.3. Registering the Service</a></li> <li><a href="#_related_services_23">3.17.4. Related Services</a></li> </ul> </li> <li><a href="#_rgsvc_spi_UrlEncodingService">3.18. <code>UrlEncodingService</code></a> <ul class="sectlevel3"> <li><a href="#_spi_18">3.18.1. SPI</a></li> <li><a href="#_implementation_17">3.18.2. Implementation</a></li> </ul> </li> <li><a href="#_rgsvc_spi_UserProfileService">3.19. <code>UserProfileService</code></a> <ul class="sectlevel3"> <li><a href="#_spi_19">3.19.1. SPI</a></li> <li><a href="#_implementation_18">3.19.2. Implementation</a></li> </ul> </li> <li><a href="#_rgsvc_spi_UserRegistrationService">3.20. <code>UserRegistrationService</code></a> <ul class="sectlevel3"> <li><a href="#_spi_20">3.20.1. SPI</a></li> <li><a href="#_implementation_19">3.20.2. Implementation</a></li> <li><a href="#_registering_the_services_21">3.20.3. Registering the Services</a></li> <li><a href="#_related_services_24">3.20.4. Related Services</a></li> </ul> </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" === "Domain Services") { 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>