You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2014/02/12 00:31:11 UTC
[14/51] [partial] ISIS-694: mothballing the docbkx folders.
http://git-wip-us.apache.org/repos/asf/isis/blob/7a7836e3/mothballed/docbkx/core/applib/src/docbkx/guide/isis-applib.xml
----------------------------------------------------------------------
diff --git a/mothballed/docbkx/core/applib/src/docbkx/guide/isis-applib.xml b/mothballed/docbkx/core/applib/src/docbkx/guide/isis-applib.xml
new file mode 100644
index 0000000..d88bc58
--- /dev/null
+++ b/mothballed/docbkx/core/applib/src/docbkx/guide/isis-applib.xml
@@ -0,0 +1,7488 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+"file:./src/docbkx/dtd-4.5/docbookx.dtd">
+<!--
+ 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.
+-->
+<book>
+ <bookinfo>
+ <title><?eval ${docbkxGuideTitle}?></title>
+
+ <subtitle><?eval ${docbkxGuideSubTitle}?></subtitle>
+
+ <releaseinfo><?eval ${project.version}?></releaseinfo>
+
+ <authorgroup>
+ <author>
+ <firstname>Dan</firstname>
+
+ <surname>Haywood</surname>
+ </author>
+
+ <author>
+ <firstname>Robert</firstname>
+
+ <surname>Matthews</surname>
+ </author>
+ </authorgroup>
+
+ <legalnotice>
+ <para>Permission is granted to make and distribute verbatim copies of
+ this manual provided that the copyright notice and this permission
+ notice are preserved on all copies.</para>
+ </legalnotice>
+ </bookinfo>
+
+ <!-- front matter -->
+
+ <toc></toc>
+
+ <preface id="preface">
+ <title>Preface</title>
+
+ <para><emphasis>Apache Isis</emphasis> is designed to allow programmers
+ rapidly develop domain-driven applications following the <ulink
+ url="http://en.wikipedia.org/wiki/Naked_Objects">Naked Objects</ulink>
+ pattern. It is made up of a core plus a number of components for each of
+ the main APIs: objectstores, security, viewers and profilestores.</para>
+
+ <para>This guide is written for programmers looking to understand the
+ programming conventions, annotations and supporting utilities within the
+ <emphasis>Apache Isis</emphasis> application library (or
+ <emphasis>applib</emphasis>), in order that the framework can correctly
+ pick up and render the business rules and logic encoded within their
+ domain objects.</para>
+
+ <para><emphasis>Apache Isis</emphasis> is hosted at the <ulink
+ url="http://incubator.apache.org/isis">Apache Foundation</ulink>, and is
+ licensed under <ulink
+ url="http://www.apache.org/licenses/LICENSE-2.0.html">Apache Software
+ License v2</ulink>.</para>
+ </preface>
+
+ <!-- main content -->
+
+ <part id="prt.WritingDomainObjects">
+ <title>Writing Domain Objects</title>
+
+ <partintro>
+ <para>The conventions of the programming model are best described as
+ 'intentional' - they convey an intention as to how domain objects, their
+ properties and behaviours, are to be made available to users. The
+ specific way in which those intentions are interpreted or implemented
+ will depend upon the framework, and/or the particular components or
+ options selected within that framework.</para>
+
+ <para>To pick a single example, marking up a domain class with the
+ annotation <code>@Bounded</code> is an indication that the class is
+ intended to have only a small number of instances and that the set does
+ not change very often - such as the class <code>Country</code>. This is
+ an indication to a viewer, for example, that the whole set of instances
+ might be offered to the user in a convenient form such as a drop-down
+ list. The programming convention has <emphasis>not</emphasis> been
+ defined as <code>@DropDownList</code> because any equivalent mechanism
+ will suffice: a viewer might not support drop-down-lists but instead
+ might provide a capability to select from an <code>@Bounded</code> class
+ by typing the initial letters of the desired instance.</para>
+
+ <para>This part of the guide is a set of chapters that provides how-to's
+ for writing domain objects, by which we mean domain entities, value
+ types, services and repositories/factories.</para>
+ </partintro>
+
+ <chapter id="chp.Objects">
+ <title>How to write a basic Domain Entity or Service</title>
+
+ <abstract>
+ <para>How-to write a basic domain entity or service, specifying its
+ properties, collections and actions, and using some of the most
+ commonly-used additional semantics.</para>
+ </abstract>
+
+ <para>Domain entities are instances of some class, usually (the vast
+ majority) being persisted. Domain services are singletons that act
+ typically act as factories and repositories. Domain entities have state
+ in the form of properties and collections; domain services do not. Both
+ domain entities and services have behaviour, in the form of
+ actions.</para>
+
+ <sect1>
+ <title>How to have a domain entity be a POJO (not inherit from
+ framework superclasses)</title>
+
+ <para>It isn't mandatory for domain entities to inherit from any
+ framework superclass; they can be plain old java objects (pojos) if
+ required. However, they do at a minimum need to have a
+ <classname>org.apache.isis.applib.DomainObjectContainer</classname>
+ injected into them (an interface), from which other framework services
+ can be accessed.</para>
+
+ <para>If you don't have a requirement to inherit from any other
+ superclass, then it usually makes sense to inherit from mention
+ <classname>org.apache.isis.applib.AbstractDomainObject</classname>,
+ which already supports the
+ <classname>DomainObjectContainer</classname> and has a number of
+ convenience helper methods.</para>
+
+ <para>There is further coverage of
+ <classname>DomainObjectContainer</classname> in <xref
+ linkend="sec.LifecycleMethods" /> and also in <xref
+ linkend="chp.DomainServices" />.</para>
+ </sect1>
+
+ <sect1 id="sec.AbstractService">
+ <title>How to have a domain service be a POJO (not inherit from
+ framework superclasses)</title>
+
+ <para>Like entities, it isn't mandatory for domain services to inherit
+ from any framework superclass; they can be plain-old pojos if
+ required. However, again, like entities, they do at a minimum need to
+ have a
+ <classname>org.apache.isis.applib.DomainObjectContainer</classname>
+ injected into them (an interface), from which other framework services
+ can be accessed.</para>
+
+ <para>If you don't have a requirement to inherit from any other
+ superclass, then it usually makes sense to inherit from one of the
+ abstract classes in the applib, either
+ <classname>org.apache.isis.applib.AbstractService</classname> or
+ <classname>org.apache.isis.applib.AbstractRepositoryAndFactory</classname>.
+ These already supports the
+ <classname>DomainObjectContainer</classname> and have a number of
+ convenience helper methods.</para>
+
+ <para>The <acronym>UML</acronym> class diagram below shows the
+ relationship between these types and the
+ <classname>DomainObjectContainer</classname>.</para>
+
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="images/AbstractContainedObject-hierarchy.png"
+ scale="65" />
+ </imageobject>
+ </mediaobject>
+
+ <para>What this means is that <emphasis>Apache Isis</emphasis> treats
+ factories and repositories as just another type of domain
+ service.</para>
+ </sect1>
+
+ <sect1>
+ <title>How to add a property to a domain entity</title>
+
+ <para>A property is a scalar attribute or field of a domain entity.
+ Its type can be either a value type (such as an int, Date or String),
+ or a reference to another entity.</para>
+
+ <para>Properties are specified using the JavaBean conventions,
+ recognizing a standard accessor/mutator pair (<code>get</code> and
+ <code>set</code>).</para>
+
+ <para>The syntax is:</para>
+
+ <para><programlisting>public PropertyType getPropertyName()
+
+public void setPropertyName(PropertyType param)</programlisting></para>
+
+ <para>where <literal moreinfo="none">PropertyType</literal> is a
+ primitive, a value object or an entity object.</para>
+
+ <para>Properties may either be for a value type or may reference
+ another entity. Values include Java primitives, and JDK classes with
+ value semantics (eg <literal
+ moreinfo="none">java.lang.Strings</literal> and <literal
+ moreinfo="none">java.util.Dates</literal>; see <xref
+ linkend="chp.ValueTypes" /> for the full list). It is also possible to
+ write your own value types (see <xref linkend="chp.ValueTypes" />). A
+ property referencing another domain object is sometimes called an
+ association.</para>
+
+ <para>For example, the following example contains both a value
+ (<literal moreinfo="none">String</literal>) property and a reference
+ (<literal moreinfo="none">Organisation</literal>) property:</para>
+
+ <programlisting format="linespecific">public class Customer {
+
+ private String firstName;
+ public String getFirstName() {
+ return firstName;
+ }
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+
+ private Organisation organisation;
+ public Organisation getOrganisation() {
+ return organisation;
+ }
+ public void setOrganisation(Organisation organisation) {
+ this.organisation = organisation;
+ }
+
+ ...
+}</programlisting>
+ </sect1>
+
+ <sect1>
+ <title condition="java">How to specify a title for a domain
+ entity</title>
+
+ <para>A title is used to identify an object to the user in the user
+ interface. For example, a <classname>Customer</classname>'s title
+ might be the organization's customer reference, or perhaps (more
+ informally) their first and last names.</para>
+
+ <para>By default, the framework will use the object's <literal
+ moreinfo="none">toString()</literal> method as the title. Most titles
+ tend to be made up of the same set of elements: for example a
+ Customer's name might be the concatenation of their customer first
+ name and their ;ast name. For these the <classname>@Title</classname>
+ annotation can be used:</para>
+
+ <para><programlisting>public class Customer {
+ @Title
+ public String getFirstName() { ... }
+ @Title
+ public String getLastName() { ... }
+ ...
+}</programlisting></para>
+
+ <para>For more control, the order of the title components can be
+ specified using a sequence number (specified in Dewey decimal
+ format):</para>
+
+ <programlisting>public class Customer {
+ @Title("1.0")
+ public String getFirstName() { ... }
+ @Title("1.1")
+ public String getLastName() { ... }
+ ...
+}</programlisting>
+
+ <para>For more control the title can be declared imperately using the
+ <literal moreinfo="none">title()</literal> method (returning a
+ <literal moreinfo="none">String</literal>). This leaves the programmer
+ needs to make use of the <literal moreinfo="none">toString()</literal>
+ method for other purposes, such as for debugging. For example, to
+ return the title for a customer which is their last name and then
+ first initial of their first name, we could use:</para>
+
+ <para><programlisting>public class Customer {
+ public String title() {
+ return getLastName() + ", " + getFirstName().substring(0,1);
+ }
+ ...
+}</programlisting></para>
+
+ <para>The applib contains a class,
+ <classname>org.apache.isis.applib.util.TitleBuffer</classname>, which
+ you can use to help create title strings if you so wish. See <xref
+ linkend="apx.UtilityClasses" /> for more details.</para>
+ </sect1>
+
+ <sect1>
+ <title>How to add a collection to a domain entity</title>
+
+ <para>A collection is a multi-valued attribute/field of a entity, in
+ other words a <code>List</code> or a <code>Set</code>, containing
+ references to other domain entities. <code>Map</code>s are not
+ supported. Collections of value types are not supported.</para>
+
+ <para>A collection is recognized via an accessor/mutator method pair
+ (<code>get</code> and set) for any type of collection provided by the
+ programming language.</para>
+
+ <para>The syntax is either:</para>
+
+ <para><programlisting>public Collection<EntityType> getCollectionName()
+private void setCollectionName(Collection<EntityType> param)</programlisting></para>
+
+ <para>or:</para>
+
+ <para><programlisting>public List<EntityType> getCollectionName()
+private void setCollectionName(List<EntityType> param)</programlisting></para>
+
+ <para>or:</para>
+
+ <para><programlisting>public Set<EntityType> getCollectionName()
+private void setCollectionName(Set<EntityType> param)</programlisting></para>
+
+ <para>A mutator is required, but it need only have
+ <code>private</code> visibility.</para>
+
+ <para>Note:</para>
+
+ <note>
+ <para><classname>Map</classname>s cannot be used for
+ collections.</para>
+ </note>
+
+ <para>It is recommended that the collections be specified using
+ generics (for example: <literal
+ moreinfo="none">List<Customer></literal> ). That way the
+ framework will be able to display the collection based on that type
+ definition. If a raw type is used then the framework will attempt to
+ infer the type from the addToXxx() / removeFromXxx() supporting
+ methods, if specified (see <xref linkend="sec.AddToRemoveFrom" />). If
+ the framework is unable to determine the type of the collection, it
+ will mean that some viewers will represent the collection is a less
+ sophisticated manner (eg a simple list of titles rather than a
+ table).</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Employee { ... }
+
+public class Department {
+ private List<Employee> employees = new ArrayList<Employee>();
+ public List <Employee> getEmployees() {
+ return employees;
+ }
+ private void setEmployees(List<Employee> employees) {
+ this.employees = employees;
+ }
+ ...
+}</programlisting>
+ </sect1>
+
+ <sect1>
+ <title>How to add an action (or bulk action) to a domain entity or
+ service</title>
+
+ <para>An 'action' is a method that we expect the user to be able to
+ invoke on a domain entity via the user interface, though it may also
+ be invoked programmatically within the object model. The following
+ conventions are used to determine when and how methods are made
+ available to the user as actions, and the means by which a user can
+ interact with those actions</para>
+
+ <para>By default, any <literal moreinfo="none">public</literal>
+ instance method that you add to a class will be treated as a user
+ action, unless it represents a property, collection, or another
+ reserved method defined in this guide.</para>
+
+ <para>The syntax is:</para>
+
+ <para><programlisting>public void actionName([ValueOrEntityType param] ...)</programlisting></para>
+
+ <para>or</para>
+
+ <para><programlisting>public ReturnType actionName([ValueOrEntityType param] ...)</programlisting></para>
+
+ <para>When a method returns a reference the viewer will attempt to
+ display that object. If the return value is <code>null</code> then
+ nothing is displayed.</para>
+
+ <para>We refer to all methods that are intended to be invoked by users
+ as 'action methods'.</para>
+
+ <para>If you have a method that you don't want to be made available as
+ a user-action you can either:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>make it non-<literal>public</literal> (eg <literal
+ moreinfo="none">protected</literal> or <literal
+ moreinfo="none">private</literal>)</para>
+ </listitem>
+
+ <listitem>
+ <para>annotate it with <classname>@Ignore</classname></para>
+ </listitem>
+
+ <listitem>
+ <para>annotate it with <classname>@Hidden</classname> (discussed
+ further in <xref linkend="sec.HiddenActions" />)</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>Note also that <literal moreinfo="none">static</literal> methods
+ are ignored: such functionality should reside in a service, such as a
+ repository or factory (see <xref
+ linkend="chp.DomainServices" />).</para>
+
+ <para>If the action is a bulk action - meaning that it should only be
+ applied to a collection of instances of the entity - then annotate
+ using <classname>@Bulk</classname>:</para>
+
+ <programlisting>@Bulk
+public void actionName() { ... }</programlisting>
+
+ <para>Note that bulk actions have a couple of important
+ restrictions.</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>entity actions cannot take any arguments, while contributed
+ actions can take only a single parameter (the contributee)</para>
+
+ <para>This restriction might be lifted in the future;</para>
+ </listitem>
+
+ <listitem>
+ <para>any business rules for hiding, disabling or validating the
+ action are ignored.</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>See <xref linkend="chp.BusinessRules" /> for more details on
+ writing business rules.</para>
+
+ <para>At the time of writing, only the Wicket viewer recognizes bulk
+ actions; other viewers treat the action as a regular action.</para>
+ </sect1>
+
+ <sect1 id="sec.HowToSpecifyTheIconForAnObjectsClass">
+ <title condition=".net">How to specify the icon for a domain
+ entity</title>
+
+ <para condition="1.5">By default, the framework will look for an image
+ in the <filename class="directory" moreinfo="none">images</filename>
+ directory (either from the classpath or from the filesystem) that has
+ the same name as the object class. Multiple file extensions are
+ searched for, including <filename>.png</filename>,
+ <filename>.gif</filename> and <filename>.jpg</filename> (in order of
+ preference). For example, fan object of type
+ <classname>Customer</classname> it will look for <filename
+ class="directory" moreinfo="none">Customer.png</filename>, <filename
+ class="directory" moreinfo="none">Customer.gif</filename>,
+ <filename>Customer.jpg</filename> etc.</para>
+
+ <para condition="1.5">If the framework finds no such file, then it
+ will work up the inheritance hierarchy to see if there is an icon
+ matching the name of any of the super-classes, and use that instead.
+ If no matching icon is found then the framework will look for an image
+ called <filename class="directory"
+ moreinfo="none">default.png</filename>,
+ <filename>default.gif</filename> or <filename>default.jpg</filename>
+ in the images directory, and if this has not been specified, then the
+ framework will use its own default image for an icon.</para>
+
+ <para>We strongly recommend that you adopt 'pascal case' as the
+ convention for icon file names: if you have a class called <classname
+ condition="vb"> OrderLine</classname>, then call the icon <filename
+ class="directory" moreinfo="none">OrderLine.png</filename>. Actually,
+ the framework will also recognise <filename class="directory"
+ moreinfo="none">orderline.png</filename>, but some operating systems
+ and deployment environments are case sensitive, so it is good practice
+ to adopt an unambiguous convention.</para>
+
+ <para>Alternatively, you can use the <literal
+ moreinfo="none">iconName</literal>() method instead:</para>
+
+ <programlisting format="linespecific">public String iconName() {
+ return "Person";
+}</programlisting>
+
+ <para>This makes it easy for more than one class to use the same icon,
+ without having to duplicate the image file.</para>
+ </sect1>
+
+ <sect1 id="sec.MemberOrderForProperties">
+ <title>How to specify the order in which properties or collections are
+ displayed</title>
+
+ <para>The <literal moreinfo="none">@MemberOrder</literal> annotation
+ provides a hint to the viewer as to the order in which the properties
+ and collections should appear in the <acronym>GUI</acronym>.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Customer() {
+ @MemberOrder("1")
+ public String getFirstName() { ... }
+ ...
+
+ @MemberOrder("2")
+ public String getLastName() { ... }
+ ...
+
+ @MemberOrder("3")
+ public Collection<Order> getOrders() { ... }
+ ...
+}</programlisting>
+ </sect1>
+
+ <sect1>
+ <title>How to specify the order in which actions appear on the
+ menu</title>
+
+ <para>The <literal moreinfo="none">@MemberOrder</literal> annotation
+ provides a hint to the viewer as to the order in which the actions
+ should be displayed, eg in a menu.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Customer {
+ @MemberOrder("3")
+ public void placeOrder(Product p) { ... }
+ ...
+
+ @MemberOrder("4")
+ public void blackList() { ... }
+ ...
+}</programlisting>
+
+ <para>The syntax for the <classname>@MemberOrder</classname> is dewey
+ decimal notation, so "3.5" and "3.6" come between "3" and "4"; "3.5.1"
+ comes between "3.5" and "3.6".</para>
+ </sect1>
+
+ <sect1>
+ <title>How to make a property optional (when saving an object)</title>
+
+ <para>By default, when a new transient (unsaved) object is presented
+ to the user, values must be specified for all properties before the
+ object may be saved.</para>
+
+ <para>To specify that a particular property is optional, use the
+ <literal moreinfo="none">@Optional</literal> annotation.</para>
+ </sect1>
+
+ <sect1>
+ <title>How to make an action parameter optional</title>
+
+ <para>By default, the framework assumes that when an action method is
+ to be invoked, all the parameters are mandatory. You may override this
+ behaviour by marking up one or more of the paramaters with the
+ <literal moreinfo="none">@Optional</literal> annotation.</para>
+ </sect1>
+
+ <sect1 id="sec.SizeProperties">
+ <title>How to specify the size of <classname>String</classname>
+ properties</title>
+
+ <para>Use:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>the <literal moreinfo="none">@MaxLength</literal> to specify
+ the maximum number of characters that may be stored within a
+ <classname>String</classname> property.</para>
+ </listitem>
+
+ <listitem>
+ <para>the <literal moreinfo="none">@TypicalLength</literal> to
+ specify the typical number of characters that are likely to be
+ stored within a <classname>String</classname> property. Viewers
+ are expected to use this as a hint as to the size of the field to
+ render for the property.</para>
+ </listitem>
+
+ <listitem>
+ <para>the <literal moreinfo="none">@MultiLine</literal> annotation
+ as a hint to indicate that the property should be displayed over
+ multiple lines (eg as a text area rather than a text
+ field).</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Ticket {
+ @TypicalLength(50)
+ @MaxLength(255)
+ public String getDescription() { ... }
+ ...
+
+ @MaxLength(2048)
+ @MultiLine
+ public String getNotes() { ... }
+ ...
+}</programlisting>
+ </sect1>
+
+ <sect1>
+ <title>How to specify the size of <classname>String</classname> action
+ parameters</title>
+
+ <para>As for properties (see <xref linkend="sec.SizeProperties" />),
+ use:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>the <literal moreinfo="none">@MaxLength</literal> to specify
+ the maximum number of characters that may be stored within a
+ <classname>String</classname> parameter.</para>
+ </listitem>
+
+ <listitem>
+ <para>the <literal moreinfo="none">@TypicalLength</literal> to
+ specify the typical number of characters that are likely to be
+ stored within a <classname>String</classname> parameter. Viewers
+ are expected to use this as a hint as to the size of the field to
+ render for the parameter.</para>
+ </listitem>
+
+ <listitem>
+ <para>the <literal moreinfo="none">@MultiLine</literal> annotation
+ as a hint to indicate that the parameter should be displayed over
+ multiple lines (eg as a text area rather than a text
+ field).</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class TicketRaiser {
+
+ public void raiseTicket(
+ @TypicalLength(50) @MaxLength(255) @Named("Description")
+ String getDescription,
+ @MaxLength(2048) @MultiLine @Named("Notes")
+ String notes) {
+ ...
+ }
+ ...
+}</programlisting>
+ </sect1>
+
+ <sect1 id="sec.ActionParameterNames">
+ <title>How to specify names and/or descriptions for an action
+ parameter</title>
+
+ <para>Unlike with properties, the framework cannot pick up the names
+ of parameters that you use within the domain code. By default
+ parameters will be labelled only with the type of the object required
+ (e.g. 'String:' or 'Customer:)</para>
+
+ <para>If you want a parameter to have a different name (such as 'First
+ Name', 'Last Name') then that parameter should be marked up with an
+ <literal moreinfo="none">@Named</literal> annotation - very often
+ taking the same form as the parameter name used in the code.
+ Alternatively though, you could create a user-defined value type,
+ using <literal>@Value</literal> (see <xref
+ linkend="chp.ValueTypes" />).</para>
+
+ <para>Similarly, any parameter may be given a short user-description
+ using the <literal moreinfo="none">@DescribedAs</literal> annotation.
+ The framework takes responsibility to make this available to the
+ user.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Customer {
+ public Order placeOrder(
+ Product p,
+ @Named("Quantity")
+ @DescribedAs("The number of units of the specified product in this order")
+ int quantity) {
+ ...
+ }
+ ...
+}</programlisting>
+ </sect1>
+
+ <sect1>
+ <title>How to inject services into a domain entity or other
+ service</title>
+
+ <para>All that is required to inject a service into an entity (or
+ indeed into another service) is to provide an appropriately typed
+ setter. The name of the method does not matter, only that it is
+ prefixed "set", is public, and has a single parameter of the correct
+ type.</para>
+
+ <para>For example:</para>
+
+ <programlisting>public class Customer {
+ private OrderRepository orderRepository;
+ public void setOrderRepository(OrderRepository orderRepository) {
+ this.orderRepository = orderRepository;
+ }
+ ...
+}</programlisting>
+
+ <para>Note that we consider
+ <classname>DomainObjectContainer</classname> to be a service too;
+ hence it can be injected in exactly the same manner.</para>
+ </sect1>
+
+ <sect1 id="sec.HowToCreateAnObject">
+ <title>How to create or delete objects within your code</title>
+
+ <para>When you create any domain object within your application code,
+ it is important that the framework is made aware of the existence of
+ this new object - in order that it may be persisted to the object
+ store, and in order that any services that the new object needs are
+ injected into it.</para>
+
+ <para>Just specifying <literal moreinfo="none">new
+ Customer()</literal>, for example, will create a
+ <classname>Customer</classname> object, but that object will
+ <emphasis>not</emphasis> be known to the framework. However, since we
+ do not want to tie our domain objects to a particular framework, we
+ use the idea of a 'container' to mediate, specified by the <literal
+ moreinfo="none">org.apache.isis.applib.DomainObjectContainer</literal>
+ interface. See <xref linkend="apx.DomainObjectContainer" /> for the
+ full list of methods provided by
+ <classname>DomainObjectContainer</classname>.</para>
+
+ <para>This interface defines the following methods for managing domain
+ objects:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><literal moreinfo="none"><T> T
+ newTransientInstance(final Class<T>
+ ofClass)</literal></para>
+
+ <para>Returns a new instance of the specified class, that is
+ transient (unsaved). The object may subsequently be saved either
+ by the user invoking the Save action (that will automatically be
+ rendered on the object view) or programmatically by calling
+ <literal moreinfo="none">persist(Object
+ transientObject)</literal></para>
+ </listitem>
+
+ <listitem>
+ <para><methodname><T> T newPersistentInstance(final
+ Class<T> ofClass)</methodname></para>
+
+ <para>Creates a new object already persisted.</para>
+ </listitem>
+
+ <listitem>
+ <para><methodname>boolean isPersistent()</methodname></para>
+
+ <para>Checks whether an object has already been persisted. This is
+ often useful in controlling visibility or availability of
+ properties or actions.</para>
+ </listitem>
+
+ <listitem>
+ <para><methodname>void persist(Object
+ transientObject)</methodname></para>
+
+ <para>Persists a transient object (created using
+ <methodname>newTransientInstance(...)</methodname>, see
+ above).</para>
+ </listitem>
+
+ <listitem>
+ <para><methodname>void persistIfNotAlready(Object
+ domainObject)</methodname></para>
+
+ <para>It is an error to persist an object if it is already
+ persistent; this method will persist only if the object is not
+ already persistent (otherwise it will do nothing).</para>
+ </listitem>
+
+ <listitem>
+ <para><methodname>void remove(Object
+ persistentObject)</methodname></para>
+
+ <para>Removes (deletes) from the object store, making the
+ reference transient.</para>
+ </listitem>
+
+ <listitem>
+ <para><methodname>void removeIfNotAlready(Object
+ domainObject)</methodname></para>
+
+ <para>It is an error to remove an object if it is not persistent;
+ this method will remove only if the object is known to be
+ persistent (otherwise it will do nothing).</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>A domain object specifies that it needs to have a reference to
+ the <classname>DomainObjectContainer</classname> injected into by
+ including the following code:</para>
+
+ <programlisting format="linespecific">private DomainObjectContainer container;
+protected DomainObjectContainer getContainer() {
+ return container;
+}
+public final void setContainer(final DomainObjectContainer container) {
+ this.container = container;
+}</programlisting>
+
+ <para>Creating or deleting objects is then done by invoking those
+ methods on the container. For example the following code would then
+ create a new <classname>Customer</classname> object within another
+ method:</para>
+
+ <programlisting format="linespecific">Customer newCust = getContainer().newTransientInstance(Customer.class);
+newCust.setName("Charlie");
+getContainer().persist(newCust);</programlisting>
+
+ <para>If you are able to make your domain object inherit from <literal
+ moreinfo="none">org.apache.isis.applib.AbstractDomainObject</literal>
+ then you have direct access to those methods, so the code would
+ become:</para>
+
+ <programlisting format="linespecific">Customer newCust = newTransientInstance(Customer.class);
+newCust.setName("Charlie");
+persist(newCust);</programlisting>
+
+ <para>As an alternative to putting the creation logic within your
+ domain objects, you could alternatively delegate to an injected
+ factory (see <xref linkend="chp.DomainServices" />). Ultimately
+ factories just delegate back to
+ <classname>DomainObjectContainer</classname> in the same way, so from
+ a technical standpoint there is little difference. However it is
+ generally worth introducing a factory because it provides a place to
+ centralize any business logic. It also affords the opportunity to
+ introduce a domain term (eg <classname>ProductCatalog</classname> or
+ <classname>StudentRegister</classname>), thereby reinforcing the
+ "ubiquitous language".</para>
+
+ <para>These methods are actually provided by the
+ <classname>org.apache.isis.applib.AbstractContainedObject</classname>
+ and so are also available on <literal
+ moreinfo="none">org.apache.isis.applib.AbstractService</literal> (and,
+ hence, on <literal
+ moreinfo="none">org.apache.isis.applib.AbstractFactoryAndRepository</literal>)
+ for creating objects within a service.</para>
+
+ <warning>
+ <para>It is possible to create a transient object within another
+ transient object. When the framework persists any transient object,
+ by default it will automatically persist any other transient object
+ referenced by that object. However, if any of these transient
+ objects are to be exposed to the user (while in their transient
+ state), then you need to write your code very carefully -
+ anticipating the fact that the user could elect to save any of the
+ transient objects at any point - which could cause the graph of
+ related objects to be persisted in an invalid state.</para>
+
+ <para>The recommended approach is, if possible, to mark these
+ supplementary classes as not persistable by the user (see <xref
+ linkend="not-persistable" />), or not to permit the user to create a
+ new transient object that is a child of an existing transient
+ object, but, rather, to require the user to save the parent object
+ first.</para>
+ </warning>
+ </sect1>
+ </chapter>
+
+ <chapter id="chp.BusinessRules">
+ <title>How to add business rules</title>
+
+ <abstract>
+ <para>How-to add business rules to domain entities and services,
+ controlling whether a domain entity or service's class members are
+ visible, if they are enabled, and to validate arguments.</para>
+ </abstract>
+
+ <para>Business rules can be added to domain objects in a number of ways.
+ As well as the business logic encapsulated by domain object actions, the
+ framework also supports a number of conventions that allow a domain
+ entity or service's members to be made visible or hidden, to be enabled
+ or disabled (greyed out), and to validate arguments when invoking an
+ action, setting a new value for a property, or if adding a new element
+ to a collection.</para>
+
+ <para>Or, in other words: "see it, use it, do it".</para>
+
+ <sect1 id="sec.HiddenProperty">
+ <title>How to hide a property</title>
+
+ <para>The mechanism for hiding a property is broadly the same as for
+ hiding a collection (see <xref linkend="sec.HiddenCollection" />) or
+ an action (see <xref linkend="sec.HiddenActions" />).</para>
+
+ <para>For control over the entire object, see <xref
+ linkend="sec.Entity.Hidden" />.</para>
+
+ <sect2>
+ <title>Hiding a property always</title>
+
+ <para>To prevent a user from viewing a property at all, use the
+ <literal moreinfo="none">@Hidden</literal> annotation. A common use
+ case is to hide an internal Id, eg perhaps as required by the object
+ store.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class OrderLine {
+ private Integer id;
+ @Hidden
+ public Integer getId() { ... }
+ public void setId(Integer id) { ... }
+ ...
+}</programlisting>
+ </sect2>
+
+ <sect2>
+ <title>Hiding a property based on the persistence state of the
+ object</title>
+
+ <para>As a refinement of the above, a property may be optionally
+ hidden using the <classname>@Hidden</classname> annotation based on
+ the persistence state of the object:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>to hide the property when the object is transient, use
+ <code>@Hidden(When.UNTIL_PERSISTED)</code></para>
+ </listitem>
+
+ <listitem>
+ <para>to hide the property when the object is persistent, use
+ <code>@Hidden(When.ONCE_PERSISTED)</code></para>
+ </listitem>
+ </itemizedlist>
+ </sect2>
+
+ <sect2>
+ <title>Hiding a property under certain conditions</title>
+
+ <para>A <literal moreinfo="none">hideXxx()</literal> method can be
+ used to indicate that a particular object's property should be
+ hidden under certain conditions, typically relating to the state of
+ that instance.</para>
+
+ <para>The syntax is:</para>
+
+ <programlisting><literal moreinfo="none">public boolean hidePropertyName()</literal> </programlisting>
+
+ <para>Returning a value of <code>true</code> indicates that the
+ property should be hidden.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Order {
+ public String getShippingInstructions() { ... }
+ public void setShippingInstructions(String shippingInstructions) { ... }
+ public boolean hideShippingInstructions() {
+ return hasShipped();
+ }
+ ...
+}</programlisting>
+ </sect2>
+
+ <sect2>
+ <title>Hiding a property for specific users or roles</title>
+
+ <para>It is possible to hide properties for certain users/roles by
+ calling the <methodname>DomainObjectContainer#getUser()</methodname>
+ method. See <xref
+ linkend="sec.BusinessRulesForCertainUsersOrRoles" />for further
+ discussion.</para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="sec.HiddenCollection">
+ <title>How to hide a collection</title>
+
+ <para>The mechanism for hiding a collection is broadly the same as for
+ hiding a property (see <xref linkend="sec.HiddenProperty" />) or an
+ action (see <xref linkend="sec.HiddenActions" />).</para>
+
+ <sect2>
+ <title>Hiding a collection permanently</title>
+
+ <para>To prevent a user from viewing a collection at all, use the
+ <literal moreinfo="none">@Hidden</literal> annotation.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Order {
+ private List<Order> cancelledOrders = new ArrayList<Order>();
+ @Hidden
+ public List<Order> getCancelledOrders() { ... }
+ private void setCancelledOrders(List<Order> cancelledOrders) { ... }
+ ...
+}</programlisting>
+ </sect2>
+
+ <sect2>
+ <title>Hiding a collection based on the persistence state of the
+ object</title>
+
+ <para>As a refinement of the above, a collection may be optionally
+ hidden using the <classname>@Hidden</classname> annotation based on
+ the persistence state of the object:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>to hide the collection when the object is transient, use
+ <code>@Hidden(When.UNTIL_PERSISTED)</code></para>
+ </listitem>
+
+ <listitem>
+ <para>to hide the collection when the object is persistent, use
+ <code>@Hidden(When.ONCE_PERSISTED)</code></para>
+ </listitem>
+ </itemizedlist>
+ </sect2>
+
+ <sect2>
+ <title>Hiding a collection under certain conditions</title>
+
+ <para>A <literal moreinfo="none">hideXxx()</literal> method can be
+ used to indicate that a particular object's collection should be
+ hidden under certain conditions, typically relating to the state of
+ that instance.</para>
+
+ <para>The syntax is:</para>
+
+ <programlisting><literal moreinfo="none">public boolean hideCollectionName()</literal> </programlisting>
+
+ <para>Returning a value of <code>true</code> indicates that the
+ collection should be hidden.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Order {
+ @Hidden
+ public List<Order> getRushOrders() { ... }
+ ...
+}</programlisting>
+ </sect2>
+
+ <sect2>
+ <title>Hiding a collection for specific users or roles</title>
+
+ <para>It is possible to hide collections for certain users/roles by
+ calling the <methodname>DomainObjectContainer#getUser()</methodname>
+ method. See <xref
+ linkend="sec.BusinessRulesForCertainUsersOrRoles" /> for further
+ discussion.</para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="sec.HiddenActions">
+ <title>How to hide an action</title>
+
+ <para>The mechanism for hiding an action is broadly the same as for
+ hiding a property (see <xref linkend="sec.HiddenProperty" />) or a
+ collection (see <xref linkend="sec.HiddenCollection" />).</para>
+
+ <sect2>
+ <title>Hiding an action permanently</title>
+
+ <para>To prevent a user from viewing an action at all, use the
+ <literal moreinfo="none">@Hidden</literal> annotation. This is
+ generally used where a <literal moreinfo="none">public</literal>
+ method on an object is not intended to be a user action</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Order {
+ @Hidden
+ public void markAsCancelled() { ... }
+ ...
+}</programlisting>
+ </sect2>
+
+ <sect2>
+ <title>Hiding an action based on the persistence state of the
+ object</title>
+
+ <para>As a refinement of the above, an action may be optionally
+ hidden using the <classname>@Hidden</classname> annotation based on
+ the persistence state of the object:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>to hide the action when the object is transient, use
+ <code>@Hidden(When.UNTIL_PERSISTED)</code></para>
+ </listitem>
+
+ <listitem>
+ <para>to hide the action when the object is persistent, use
+ <code>@Hidden(When.ONCE_PERSISTED)</code></para>
+ </listitem>
+ </itemizedlist>
+ </sect2>
+
+ <sect2>
+ <title>Hiding an action under certain conditions</title>
+
+ <para>A <literal moreinfo="none">hideXxx()</literal> method can be
+ used to indicate that a particular object's action should be hidden
+ under certain conditions, typically relating to the state of that
+ instance.</para>
+
+ <para>The syntax is:</para>
+
+ <programlisting><literal moreinfo="none">public boolean hideActionName([ValueOrEntityType param] ...)</literal> </programlisting>
+
+ <para>where the parameter types should match the action itself.
+ Returning a value of <code>true</code> indicates that the action
+ should be hidden.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Order {
+ public void applyDiscount(int percentage) { ... }
+ public boolean hideApplyDiscount() {
+ return isWholesaleOrder();
+ }
+}</programlisting>
+ </sect2>
+
+ <sect2>
+ <title>Hiding an action for specific users or roles</title>
+
+ <para>It is possible to hide actions for certain users/roles by
+ calling the <methodname>DomainObjectContainer#getUser()</methodname>
+ method. See <xref
+ linkend="sec.BusinessRulesForCertainUsersOrRoles" /> for further
+ discussion.</para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="sec.Entity.Hidden">
+ <title>How to specify that none of an object's members is
+ visible</title>
+
+ <para>To when an object is visible, provide a hidden() method:</para>
+
+ <para><programlisting>public class TrackingAction implements Tracking {
+ public boolean hidden(){
+ ...
+ }
+}</programlisting>If the function returns true, all properties and actions
+ will be hidden from the user, similar to <xref
+ linkend="sec.HiddenProperty" />.</para>
+ </sect1>
+
+ <sect1 id="sec.DisabledProperty">
+ <title>How to prevent a property from being modified</title>
+
+ <para>Preventing the user from modifying a property value is known as
+ 'disabling' the property. Note that this doesn't prevent the property
+ from being modified programmatically.</para>
+
+ <para>The mechanism for disabling a property is broadly the same as
+ for disabling a collection (see <xref
+ linkend="sec.DisabledCollection" />) or a collection (see <xref
+ linkend="sec.DisabledCollection" />).</para>
+
+ <para>For control over the entire object, see <xref
+ linkend="sec.Entity.Disabled" />.</para>
+
+ <sect2>
+ <title>Disabling a property permanently</title>
+
+ <para>To prevent a user from being able to modify the property at
+ all, use the <literal moreinfo="none">@Disabled</literal>
+ annotation.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class OrderLine {
+ private int quantity;
+ @Disabled
+ public String getQuantity() { ... }
+ public void setQuantity(int quantity) { ... }
+ ...
+}</programlisting>
+
+ <para>Note that a setter is still required; this is used by the
+ framework to recreate the object when pulled back from the
+ persistent object store.</para>
+ </sect2>
+
+ <sect2>
+ <title>Disabling a property based on the persistence state of the
+ object</title>
+
+ <para>As a refinement of the above, a property may be optionally
+ disabled using the <classname>@Disabled</classname> annotation based
+ on the persistence state of the object:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>to disable the property when the object is transient, use
+ <code>@Disabled(When.UNTIL_PERSISTED)</code></para>
+ </listitem>
+
+ <listitem>
+ <para>to disable the property when the object is persistent, use
+ <code>@Disabled(When.ONCE_PERSISTED)</code></para>
+ </listitem>
+ </itemizedlist>
+ </sect2>
+
+ <sect2>
+ <title>Disabling a property under certain conditions</title>
+
+ <para>A supporting <literal moreinfo="none">disableXxx()</literal>
+ method can be used to disable a particular instance's member under
+ certain conditions</para>
+
+ <para>The syntax is:</para>
+
+ <programlisting><literal moreinfo="none">public String disablePropertyName()</literal></programlisting>
+
+ <para>A non-<code>null</code> return value indicates the reason why
+ the property cannot be modified. The framework is responsible for
+ providing this feedback to the user.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class OrderLine {
+ public String getQuantity() { ... }
+ public void setQuantity(int quantity) { ... }
+ public String disableQuantity() {
+ if (isSubmitted()) {
+ return "Cannot alter any quantity after Order has been submitted";
+ }
+ return null;
+ }
+}</programlisting>
+
+ <para>If there are multiple reasons to disable a property, take a
+ look at the
+ <classname>org.apache.isis.applib.util.ReasonBuffer</classname>
+ helper.</para>
+ </sect2>
+
+ <sect2>
+ <title>Disabling a property for certain users/roles</title>
+
+ <para>It is possible to disable properties for certain users/roles
+ by calling the
+ <methodname>DomainObjectContainer#getUser()</methodname> method. See
+ <xref linkend="sec.BusinessRulesForCertainUsersOrRoles" />for
+ further discussion.</para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="sec.DisabledCollection">
+ <title>How to prevent a collection from being modified</title>
+
+ <para>Preventing the user from adding to or removing from a collection
+ is known as 'disabling' the collection.</para>
+
+ <para>The mechanism for disabling a collection is broadly the same as
+ for disabling a property (see <xref linkend="sec.DisabledProperty" />)
+ or a action (see <xref linkend="sec.DisabledAction" />).</para>
+
+ <sect2>
+ <title>Disabling a collection permanently</title>
+
+ <para>Some, though not all, viewers allow the user to directly
+ manipulate the contents of a collection. For example, the DnD viewer
+ will allow new objects to be "dropped" into a collection, and
+ existing objects removed from a collection.</para>
+
+ <para>Although it is possible to associate behaviour with such
+ actions (see <xref linkend="sec.AddToRemoveFrom" />), it may be
+ preferred to only allow modification through actions. Or, the
+ application may be deployed using a viewer that doesn't fully
+ support direct manipulation of collections.</para>
+
+ <para>In either case, annotate the collection using the <literal
+ moreinfo="none">@Disabled</literal> annotation.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Order {
+ private List<Order> cancelledOrders = new ArrayList<Order>();
+ @Disabled
+ public List<Order> getCancelledOrders() { ... }
+ private void setCancelledOrders(List<Order> cancelledOrders) { ... }
+ ...
+}</programlisting>
+ </sect2>
+
+ <sect2>
+ <title>Disabling a collection based on the persistence state of the
+ object</title>
+
+ <para>As a refinement of the above, a collection may be optionally
+ disabled using the <classname>@Disabled</classname> annotation based
+ on the persistence state of the object:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>to disable the collection when the object is transient,
+ use <code>@Disabled(When.UNTIL_PERSISTED)</code></para>
+ </listitem>
+
+ <listitem>
+ <para>to disable the collection when the object is persistent,
+ use <code>@Disabled(When.ONCE_PERSISTED)</code></para>
+ </listitem>
+ </itemizedlist>
+ </sect2>
+
+ <sect2>
+ <title>Disabling a collection under certain conditions</title>
+
+ <para>A <literal moreinfo="none">disableXxx()</literal> method can
+ be used to disable a particular instance's collection under certain
+ conditions:</para>
+
+ <para>The syntax is:</para>
+
+ <programlisting><literal moreinfo="none">public String disableCollectionName()</literal> </programlisting>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Department {
+ public List<Employee> getEmployees() { ... }
+ private void setEmployees(List<Employee> employees) { ... }
+ public void disableEmployees() {
+ return isClosed()? "This department is closed" : null;
+ }
+ ...
+}</programlisting>
+ </sect2>
+
+ <sect2>
+ <title>Disabling a collection for specific users or roles</title>
+
+ <para>It is possible to disable collections for certain users/roles
+ by calling the
+ <methodname>DoymainObjectContainer#getUser()</methodname> method.
+ See <xref linkend="sec.BusinessRulesForCertainUsersOrRoles" /> for
+ further discussion.</para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="sec.DisabledAction">
+ <title>How to prevent an action from being invoked</title>
+
+ <para>Preventing the user from invoking an action is known as
+ 'disabling' the action.</para>
+
+ <para>The mechanism for disabling an action is broadly the same as for
+ disabling a property (see <xref linkend="sec.DisabledProperty" />) or
+ a collection (see <xref linkend="sec.DisabledCollection" />).</para>
+
+ <sect2>
+ <title>Disabling an action permanently</title>
+
+ <para>It is possible to prevent an action from ever being invoked
+ using the <classname>@Disabled</classname> annotation, exactly
+ equivalent to the use of the annotation for properties and
+ collections. However, it's not a particularly meaningful usecase:
+ why display an action that can never be invoked? The only reason we
+ can think of is as a placeholder during prototyping - to indicate to
+ the user that an action is planned, but has not yet been
+ implemented.</para>
+ </sect2>
+
+ <sect2>
+ <title>Disabling an action based on the persistence state of the
+ object</title>
+
+ <para>Whereas annotating an action simply as
+ <classname>@Disabled</classname> probably does not make sense (see
+ above), it does make sense to optionally disable an action using the
+ <classname>@Disabled</classname> annotation based on the persistence
+ state of the object:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>to disable the action when the object is transient, use
+ <code>@Disabled(When.UNTIL_PERSISTED)</code></para>
+ </listitem>
+
+ <listitem>
+ <para>to disable the action when the object is persistent, use
+ <code>@Disabled(When.ONCE_PERSISTED)</code></para>
+ </listitem>
+ </itemizedlist>
+ </sect2>
+
+ <sect2>
+ <title>Based on the state of the object</title>
+
+ <para>There may be circumstances in which we do not want the user to
+ be able to initiate the action at all - for example because that
+ action is not appropriate to the current state of the object on
+ which the action resides. Such rules are enforced by means of a
+ <literal moreinfo="none">disableXxx()</literal> supporting
+ method.</para>
+
+ <para>The syntax is:</para>
+
+ <para><programlisting>public String disableActionName([ValueOrEntityType param]...)</programlisting></para>
+
+ <para>A non-<literal>null</literal> return <code>String</code>
+ indicates the reason why the action may not be invoked. The
+ framework takes responsibility to provide this feedback to the
+ user.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Customer {
+ public Order placeOrder(Product p, int quantity) { ... }
+ public String disablePlaceOrder(Product p, int quantity) {
+ return isBlackListed()?
+ "Blacklisted customers cannot place orders"
+ :null;
+ }
+ ...
+}</programlisting>
+ </sect2>
+
+ <sect2>
+ <title>Disabling an action for certain users or roles</title>
+
+ <para>It is possible to disable actions for certain users/roles by
+ calling the <methodname>DomainObjectContainer#getUser()</methodname>
+ method. See <xref
+ linkend="sec.BusinessRulesForCertainUsersOrRoles" /> for further
+ discussion.</para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="sec.Entity.Disabled">
+ <title>How to specify that none of an object's members can be
+ modified/invoked</title>
+
+ <para>Some objects have state which should not be modifiable only
+ under certain conditions; for example an invoice can not be modified
+ after it has been paid. The viewer is expected to interpret this by
+ suppressing any sort of "edit" button.</para>
+
+ <para>To indicate that an object cannot be modified, use the <literal
+ moreinfo="none">String disabled(Type type)</literal> method.</para>
+
+ <para>For example:</para>
+
+ <programlisting>public class FeeInvoice implements Invoice {
+ public String disabled(Type type){
+ ...
+ }
+}</programlisting>
+
+ <para>The <code>Type</code> is from
+ <classname>org.apache.isis.applib.Identifier</classname>:</para>
+
+ <programlisting> /**
+ * What type of feature this identifies.
+ */
+ public static enum Type {
+ CLASS, PROPERTY_OR_COLLECTION, ACTION
+ }</programlisting>
+
+ <para>and provides fine grain control over disabling actions and
+ properties.</para>
+
+ <para>The return String is null if the the object (action or property)
+ is not disabled, or the reason why it is disabled, similar to <xref
+ linkend="sec.DisabledProperty" />.</para>
+
+ <para>See also <xref linkend="sec.Entity.Disabled" />.</para>
+ </sect1>
+
+ <sect1 id="sec.Immutable">
+ <title id="sec.entity.immutable">How to specify that an object is
+ immutable (that none of its members can ever be modified)</title>
+
+ <para>Some objects have state which should not be modifiable; for
+ example those representing reference data. The viewer is expected to
+ interpret this by which suppressing any sort of "edit" button.</para>
+
+ <para>To indicate that an object cannot be modified, use the <literal
+ moreinfo="none">@Immutable</literal> annotation.</para>
+
+ <para>For example:</para>
+
+ <programlisting>@Immutable
+public class ChasingLetter implements PaymentReclaimStrategy {
+ ...
+}</programlisting>
+
+ <para>See also <xref linkend="sec.Entity.Disabled" />.</para>
+ </sect1>
+
+ <sect1>
+ <title>How to validate user input for a property</title>
+
+ <sect2>
+ <title>Declarative validation</title>
+
+ <para>For properties that accept a text input string, such as
+ <code>String</code> and <code>Date</code>, there are convenient
+ mechanisms to validate and/or normalise the values typed in:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>For <literal moreinfo="none">Date</literal> and number
+ values the <literal moreinfo="none">@Mask</literal> annotation
+ may be used.</para>
+ </listitem>
+
+ <listitem>
+ <para>For <literal moreinfo="none">String</literal> properties
+ the <literal moreinfo="none">@RegEx</literal> annotation may be
+ used.</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>More complex validation can also be performed imperatively
+ (below).</para>
+ </sect2>
+
+ <sect2>
+ <title>Imperative validation</title>
+
+ <para>A supporting <literal moreinfo="none">validateXxx()</literal>
+ method is used to check that a new value for a property is
+ valid.</para>
+
+ <para>If the proffered value is deemed to be invalid then the
+ property will not be changed. A non-null return <code>String</code>
+ indicates the reason why the member cannot be modified/action be
+ invoked; the framework is responsible for providing this feedback to
+ the user.</para>
+
+ <para>The syntax is:</para>
+
+ <para><programlisting>public String validatePropertyName(PropertyType param)</programlisting></para>
+
+ <para>where <literal moreinfo="none">PropertyType</literal> is the
+ same type as that of the property itself.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Exam {
+ public int getMark() { ... }
+ public void setMark(int mark) { ... }
+ public validateMark(int mark) {
+ return !withinRange(mark)? "Mark must be in range 0 to 30":null;
+ }
+ private boolean withinRange(int mark) { return mark >= 0 && mark <= 30; }
+}</programlisting>
+ </sect2>
+ </sect1>
+
+ <sect1>
+ <title>How to validate an object being added or removed from a
+ collection</title>
+
+ <para>A <literal moreinfo="none">validateAddToXxx()</literal> method
+ can be used to check that an object is valid to be added to a
+ collection. Conversely, the
+ <methodname>validateRemoveFromXxx()</methodname> method can be used to
+ check that it is valid to remove an object from a collection is
+ valid.</para>
+
+ <para>The syntax is:</para>
+
+ <para><programlisting>public String validateAddToCollectionName(EntityType param)</programlisting></para>
+
+ <para>and</para>
+
+ <programlisting>public String validateRemoveFromCollectionName(EntityType param)</programlisting>
+
+ <para>A non-<code>null</code> return <code>String</code> indicates the
+ reason why the object cannot be added/removed, and the viewing
+ mechanism will display this to the user.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Department {
+ public List<Employee> getEmployees() { ... }
+ private void setEmployees(List<Employee> employees) { ... }
+ public String validateAddToEmployee(Employee employee) {
+ return employee.isRetired()?
+ "Cannot add retired employees to department"
+ :null;
+ ...
+}</programlisting>
+ </sect1>
+
+ <sect1>
+ <title>How to validate an action parameter argument</title>
+
+ <sect2>
+ <title>Declarative validation</title>
+
+ <para>For parameters that accept a text input string, such as
+ <code>String</code> and <code>Date</code>, there are convenient
+ mechanisms to validate and/or normalise the values typed in:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>For <literal moreinfo="none">Date</literal> and number
+ values the <literal moreinfo="none">@Mask</literal> annotation
+ may be used.</para>
+ </listitem>
+
+ <listitem>
+ <para>For <literal moreinfo="none">String</literal> parameters
+ the <literal moreinfo="none">@RegEx</literal> annotation may be
+ used.</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>More complex validation can also be performed imperatively
+ (below).</para>
+ </sect2>
+
+ <sect2>
+ <title>Imperative validation</title>
+
+ <para>A <literal moreinfo="none">validateXxx()</literal> method is
+ used to check that the set of arguments used by an action method is
+ valid. If the arguments are invalid then the framework will not
+ invoke the action.</para>
+
+ <para>The syntax is:</para>
+
+ <para><programlisting>public String validate<ActionName>([ValueOrEntityType param]...)</programlisting></para>
+
+ <para>A non-<code>null</code> return <classname>String</classname>
+ indicates the reason why the member cannot be modified/action be
+ invoked, and the viewing mechanism will display this feedback to the
+ user.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Customer {
+ public Order placeOrder(Product p, int quantity) { ... }
+ public String validatePlaceOrder(Product p, int quantity) {
+ if (p.isOutOfStock()) { return "Product is out of stock"; }
+ if (quantity <= 0) { return "Quantity must be a positive value"; }
+ return null;
+ }
+ ...
+}</programlisting>
+
+ <para>For complex validation, you may wish to use the
+ <classname>org.apache.isis.applib.util.ReasonBuffer</classname>
+ helper class.</para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="sec.MustSpecify">
+ <title>How to validate declaratively using @MustSatisfy</title>
+
+ <para>The <literal>@MustSatisfy</literal> annotation is an alternative
+ to using imperative validation, allowing validation rules to be
+ captured in an (implementation of a)
+ <classname>org.apache.isis.applib.spec.Specification</classname>.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class DomainObjectWithMustSatisfyAnnotations extends AbstractDomainObject {
+
+ private String lastName;
+ @MustSatisfy(SpecificationRequiresFirstLetterToBeUpperCase.class)
+ public String getLastName() {
+ resolve(lastName);
+ return lastName;
+ }
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ objectChanged();
+ }
+
+ public void changeLastName(
+ @MustSatisfy(SpecificationRequiresFirstLetterToBeUpperCase.class)
+ String lastName
+ ) {
+ setLastName(lastName);
+ }
+
+}</programlisting>
+
+ <para>To help you write your own
+ <classname>Specification</classname>s, there are some adapter classes
+ in <package>org.apache.isis.applib.specs</package>:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><classname>AbstractSpecification</classname>, which
+ implements <classname>Specification</classname> and takes
+ inspiration from the <ulink
+ url="http://code.google.com/p/hamcrest/">Hamcrest</ulink>
+ library's <classname>TypeSafeMatcher</classname> class</para>
+ </listitem>
+
+ <listitem>
+ <para><classname>SpecificationAnd</classname>, which allows a set
+ of <classname>Specification</classname>s to be grouped together
+ and require that <emphasis>all</emphasis> of them are
+ satisfied</para>
+ </listitem>
+
+ <listitem>
+ <para><classname>SpecificationOr</classname>, which allows a set
+ of <classname>Specification</classname>s to be grouped together
+ and require that <emphasis>at least one</emphasis> of them is
+ satisfied</para>
+ </listitem>
+
+ <listitem>
+ <para><classname>SpecificationNot</classname>, which requires that
+ the provided <classname>Specification</classname> is
+ <emphasis>not</emphasis> satisfied</para>
+ </listitem>
+ </itemizedlist>
+ </sect1>
+ </chapter>
+
+ <chapter>
+ <title>How to provide drop-downs and default values</title>
+
+ <abstract>
+ <para>How-to make actions easier to use from an end-user perspective,
+ by providing sets of choices and defaults.</para>
+ </abstract>
+
+ <para>Invoking actions or setting properties requires that the user
+ specify a valid value; of the correct type, and that passes any
+ validation rules that may have been defined. To make things are easier
+ for the user, you can provide lists of choices; viewers typically render
+ these values in a drop-down list box.</para>
+
+ <para>In a similar vein, there may be a default value for an action
+ parameter; this can also be specified.</para>
+
+ <sect1>
+ <title>How to specify a set of choices for a property</title>
+
+ <para>The simplest way to provide the user with a set of choices for a
+ property (possibly rendered as a drop-down list, for example) is to
+ ensure that the type used by the property is marked up as
+ <code>@Bounded</code> (see <xref linkend="sec.Bounded" />).</para>
+
+ <para>However, this is not always appropriate. For example you might
+ wish to provide the user with the choice of all the
+ <classname>Address</classname>es known for that
+ <classname>Customer</classname>, with the most recently-used address
+ as the default.</para>
+
+ <para>The syntax for specifying a list of choices is either:</para>
+
+ <para><programlisting>public Collection<PropertyType> choicesPropertyName()</programlisting></para>
+
+ <para>or alternatively</para>
+
+ <programlisting>public PropertyType[] choicesPropertyName()</programlisting>
+
+ <para>where <literal moreinfo="none">PropertyType</literal> is the
+ same type as that of the property itself.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Order {
+ public Address getShippingAddress() { ... }
+ public void setShippingAddress() { ... }
+ public List<Address> choicesShippingAddress() {
+ return getCustomer().allActiveAddresses();
+ }
+ ...
+}</programlisting>
+ </sect1>
+
+ <sect1>
+ <title>How to specify a set of choices for an action parameter</title>
+
+ <para>The programmer may provide a set of choices for the value of any
+ or all of the parameters of an action. These will be made available to
+ the user - for example as a drop-down list.</para>
+
+ <para>If the type of the parameter is annotated with <literal
+ moreinfo="none">@Bounded</literal>, then it is not necessary to
+ specify the choices for that parameter, as the user will automatically
+ be offered the full set to choose from.</para>
+
+ <para>If this isn't the case, then - as for defaults - there are two
+ different ways to specify parameters; either per parameter, or for all
+ parameters.</para>
+
+ <sect2>
+ <title>Per parameter syntax (preferred)</title>
+
+ <para>The per-parameter form is simpler and generally
+ preferred.</para>
+
+ <para>The syntax is:</para>
+
+ <programlisting>public List<ParameterType> choicesNActionName()</programlisting>
+
+ <para>where N indicates the 0-based parameter number.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Order {
+ public Order placeOrder(
+ Product product,
+ @Named("Quantity")
+ int quantity) {
+ ...
+ }
+ public List<Product> choices0PlaceOrder() {
+ return lastFiveProductsOrderedBy(this.getCustomer());
+ }
+ public List<Integer> choices1PlaceOrder() {
+ return Arrays.asList(1,2,3,4,5);
+ }
+ ....
+}</programlisting>
+ </sect2>
+
+ <sect2>
+ <title>All parameters syntax</title>
+
+ <para>The alternative mechanism is to supply all the parameter
+ choices in one go:</para>
+
+ <para><programlisting>public Object[] choices<ActionName>([<parameter type> param]...)</programlisting></para>
+
+ <para>returning an array, which must have one element per parameter
+ in the method signature. Each element of the array should itself
+ either be an array or a list, containing the set of values
+ representing the choices for that parameter, or <literal
+ moreinfo="none">null</literal> if there are no choices to be
+ specified for that parameter.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Order {
+ public Order placeOrder(
+ Product product,
+ @Named("Quantity")
+ int quantity) {
+ ...
+ }
+ public Object[] choicesPlaceOrder(
+ Product product,
+ int quantity) {
+ return new Object[] {
+ lastFiveProductsOrderedBy(this.getCustomer()).toArray(),
+ Arrays.asList(1,2,3,4,5)
+ };
+ }
+ ...
+}</programlisting>
+ </sect2>
+ </sect1>
+
+ <sect1 id="sec.Bounded">
+ <title>How to specify that a class of objects has a limited number of
+ instances</title>
+
+ <para>Sometimes an entity may only have a relatively small number of
+ instances, for example the types of credit cards accepted (Visa,
+ Mastercard, Amex). Viewers will typically expected to render the
+ complete set of instances as a drop down list whenever the object type
+ is used (ie as a property or action parameter).</para>
+
+ <para>To indicate that a class has a limited number of instances, use
+ the <classname>@Bounded</classname> annotation. Note that there is an
+ implied expectation is that the list of objects is small, and
+ relatively cheap to obtain from the object store.</para>
+
+ <para>An alternative way to specify a selection of objects is using
+ the <classname>choicesXxx()</classname> supporting methods.</para>
+
+ <para>For example:</para>
+
+ <programlisting>@Bounded
+public class PaymentMethod {
+ ...
+}</programlisting>
+
+ <para>Alternatively, you might want to use a (Java) <code>enum</code>,
+ because these are implicitly bounded.</para>
+ </sect1>
+
+ <sect1>
+ <title>How to find an entity (for an action parameter or property)
+ using auto-complete</title>
+
+ <para>Some viewers (eg the Wicket viewer) allow an entity to be
+ specified (as an argument for an action parameter, or as the new value
+ of a property) by enabling the user to type its title. The framework
+ then searches for a matching instance, presenting them in a drop-down
+ list.</para>
+
+ <para>This is accomplished using two annotations. The
+ <classname>@AutoComplete</classname> annotation is used on the entity
+ type itself:</para>
+
+ <programlisting>@AutoComplete(repository=Customers.class)
+public class Customer {
+ ...
+}</programlisting>
+
+ <para>The <code>repository</code> attribute indicates the class of the
+ domain service that has an autoComplete() method. This is required to
+ accept a single <classname>String</classname> and return a
+ <classname>List</classname> of candidates:</para>
+
+ <programlisting>public class Customers {
+ ...
+ @Hidden
+ public List<Property> autoComplete(String searchPhrase) {
+ return allMatches(new QueryDefault<Customer>("customers_findByName", "name", searchPhrase));
+ }
+}</programlisting>
+
+ <para>If required, a different action name than "autoComplete" can be
+ specified.</para>
+ </sect1>
+
+ <sect1>
+ <title>How to specify default values for an action parameter</title>
+
+ <para>When an action is about to be invoked, then default values can
+ be provided for any or all of its parameters.</para>
+
+ <para>There are two different ways to specify parameters; either per
+ parameter, or for all parameters.</para>
+
+ <sect2>
+ <title>Per-parameter syntax (preferred)</title>
+
+ <para>The per-parameter form is simpler and generally to be
+ preferred.</para>
+
+ <para>The syntax is:</para>
+
+ <programlisting>public ParameterType defaultNActionName()</programlisting>
+
+ <para>where N indicates the 0-based parameter number. For
+ example:</para>
+
+ <programlisting format="linespecific">public class Customer {
+ public Order placeOrder(
+ Product product,
+ @Named("Quantity")
+ int quantity) {
+ ...
+ }
+ public Product default0PlaceOrder() {
+ return productMostRecentlyOrderedBy(this.getCustomer());
+ }
+ public int default1PlaceOrder() {
+ return 1;
+ }
+}</programlisting>
+ </sect2>
+
+ <sect2>
+ <title>All parameters syntax</title>
+
+ <para>The syntax for specifying all the parameter default values in
+ one go is:</para>
+
+ <para><programlisting>public Object[] defaultActionName([ValueOrEntityType param]...)</programlisting></para>
+
+ <para>returning an array which must have one element per parameter
+ in the action method signature of corresponding default
+ values.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Customer {
+ public Order placeOrder(
+ Product product,
+ @Named("Quantity")
+ int quantity) {
+ ...
+ }
+ public Object[] defaultPlaceOrder(
+ Product product,
+ int quantity) {
+ return new Object[] {
+ productMostRecentlyOrderedBy(this.getCustomer()),
+ 1
+ };
+ }
+ ...
+}</programlisting>
+ </sect2>
+ </sect1>
+ </chapter>
+
+ <chapter>
+ <title>How to derive properties and collections, and other
+ side-effects</title>
+
+ <abstract>
+ <para>How-to derive properties and collections from persisting state,
+ and provide other side-effects.</para>
+ </abstract>
+
+ <para>The Isis viewers will automatically render the state of properties
+ and collections, but the values of such need not be persisted; they can
+ be derived from other information available to the object.</para>
+
+ <para>For collections</para>
+
+ <sect1 id="sec.DerivedProperty">
+ <title>How to make a derived property</title>
+
+ <sect2>
+ <title>Read-only</title>
+
+ <para>Most derived properties are read-only, their value being
+ derived from other information available to the object.</para>
+
+ <para>Omitting the mutator (<code>setXxx()</code>) method for a
+ property indicates both that the field is derived, and is not be
+ persisted.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Employee {
+ public Department getDepartment() { ... }
+ ...
+
+ // this is the derived property
+ public Employee getManager() {
+ if (getDepartment() == null) { return null; }
+ return getDepartment().getManager();
+ }
+ ...
+}</programlisting>
+ </sect2>
+
+ <sect2>
+ <title>Read-write</title>
+
+ <para>A derived property can be made updateable (in that it takes
+ the provided value and does something sensible with it) by providing
+ a <code>modifyXxx()</code> supporting method:</para>
+
+ <programlisting format="linespecific">public class Employee {
+ public Department getDepartment() { ... }
+ ...
+
+ // this is the derived property
+ public Employee getManager() { ... }
+
+ // this makes the derived property modifiable
+ public void modifyManager(Employee manager) {
+ if (getDepartment() == null) { return; }
+ getDepartment().modifyManager(manager);
+ }
+
+ ...
+}</programlisting>
+
+ <para>Note how the implementation of such a <code>modifyXxx()</code>
+ method typically modifies the original source of the information
+ (the <classname>Department</classname> object).</para>
+
+ <para>Alternatively, if you prefer to have a setter, then you can
+ use Isis' <classname>@NotPersisted</classname> attribute.</para>
+
+ <programlisting format="linespecific">public class Employee {
+ public Department getDepartment() { ... }
+ ...
+
+ @NotPersisted
+ public Employee getManager() { ... }
+ public void setManager(Employee manager) {
+ if (getDepartment() == null) { return; }
+ getDepartment().modifyManager(manager);
+ }
+ ...
+}</programlisting>
+
+ <note>
+ <para>If you use this approach, then for some object stores you
+ may also need to annotate the property to exclude it. For example
+ the JDO/DataNucleus object store requires the property being
+ annotated with
+ <classname>@javax.jdo.annotations.NotPersistent</classname>.</para>
+ </note>
+ </sect2>
+ </sect1>
+
+ <sect1 id="sec.DerivedCollection">
+ <title>How to make a derived collection</title>
+
+ <para>Collections can be derived by omitting the mutator (the same way
+ as properties, see <xref linkend="sec.DerivedProperty" />).</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Department {
+ // Standard collection
+ private List<Employee> employees = new ArrayList<Employee>();
+ public List<Employee> getEmployees() { ... }
+ private void setEmployees(List<Employee>) { ... }
+
+ // Derived collection
+ public List<Employee> getTerminatedEmployees() {
+ List<Employee> terminatedEmployees = new ArrayList<Employee>();
+ for(Employee e: employees) {
+ if (e.isTerminated()) {
+ addTo(terminatedEmployees, e);
+ }
+ }
+ return terminatedEmployees;
+ }
+ ...
+}</programlisting>
+
+ <para>Derived collections are not persisted, though may be modified if
+ there is an <code>addToXxx()</code> or <code>removeFromXxx()</code>
+ supporting method. As for derived properties, the implementations of
+ these mutators change the underlying data. For example:</para>
+
+ <programlisting format="linespecific">public class Department {
+ ...
+
+ public void addToTerminatedEmployees(Employee employee) {
+ employee.setTerminated(true);
+ }
+ public void removeFromTerminatedEmployees(Employee employee) {
+ employee.setTerminated(false);
+ }
+}</programlisting>
+ </sect1>
+
+ <sect1>
+ <title>How to inline the results of a query-only repository
+ action</title>
+
+ <para>While derived properties (<xref
+ linkend="sec.DerivedProperty" />) and derived collections (<xref
+ linkend="sec.DerivedCollection" />) typically "walk the graph" to
+ associated objects, there is nothing to prevent the returned value
+ being the result of invoking a repository (domain service)
+ action.</para>
+
+ <para>For example:</para>
+
+ <programlisting>public class Customer {
+ ...
+ public List<Order> getMostRecentOrders() {
+ return orderRepo.findMostRecentOrders(this, 5);
+ }
+}</programlisting>
+ </sect1>
+
+ <sect1 id="sec.ModifyAndClear">
+ <title>How to trigger other behaviour when a property is
+ changed</title>
+
+ <para>If you want to invoke functionality whenever a property is
+ changed by the user, then you should create a supporting <literal
+ moreinfo="none">modifyXxx()</literal> method and include the
+ functionality within that. The syntax is:</para>
+
+ <programlisting>public void modifyPropertyName(PropertyType param)</programlisting>
+
+ <para>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.</para>
+
+ <para>For example:</para>
+
+ <programlisting format="linespecific">public class Order() {
+ public Integer getAmount() { ... }
+ public void setAmount(Integer amount) { ... }
+ public void modifyAmount(Integer amount) {
+ setAmount(amount);
+ addToTotal(amount);
+ }
+ ...
+}</programlisting>
+
+ <para>Here the <literal moreinfo="none">modifyAmount()</literal>
+ method also calls the <literal moreinfo="none">addToTotal()</literal>
+ call as well as the <literal moreinfo="none">setAmount()</literal>
+ method. We don't want this <methodname>addToCall()</methodname> method
+ to be called when pulling the object back from the object store, so we
+ put it into the modify, not the setter.</para>
+
+ <para>You may optionally also specify a <code>clearXxx()</code> which
+ works the same way as modify <literal moreinfo="none">modify</literal>
+ <literal moreinfo="none">Xxx()</literal> but is called when the
+ property is cleared by the user (i.e. the current value replaced by
+ nothing). The syntax is:</para>
+
+ <programlisting>public void clearPropertyName()</programlisting>
+
+ <para>To extend the above example:</para>
+
+ <programlisting format="linespecific">public class Order() {
+ public Integer getAmount() { ... }
+ public void setAmount(Integer amount) { ... }
+ public void modifyAmount(Integer amount) { ... }
+ public void clearAmount() {
+ removeFromTotal(this.amount);
+ setAmount(null);
+ }
+ ...
+}</programlisting>
+ </sect1>
+
+ <sect1 id="sec.AddToRemoveFrom">
+ <title>How to
<TRUNCATED>