content/versions/1.12.1/guides/ugbtb.html (5,388 lines of code) (raw):

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