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 2010/10/22 16:51:49 UTC
svn commit: r1026350 [2/6] - in /incubator/isis/trunk: ./
alternatives/bytecode/ alternatives/bytecode/identity/src/site/
alternatives/bytecode/javassist/src/site/
alternatives/bytecode/src/docbkx/guide/ alternatives/bytecode/src/site/
alternatives/obj...
Modified: incubator/isis/trunk/applib/src/docbkx/guide/isis-applib.xml
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/applib/src/docbkx/guide/isis-applib.xml?rev=1026350&r1=1026349&r2=1026350&view=diff
==============================================================================
--- incubator/isis/trunk/applib/src/docbkx/guide/isis-applib.xml (original)
+++ incubator/isis/trunk/applib/src/docbkx/guide/isis-applib.xml Fri Oct 22 14:51:42 2010
@@ -23,7 +23,7 @@
<bookinfo>
<title><?eval ${docbkxGuideTitle}?></title>
- <subtitle>Programming conventions, annotations and utilities</subtitle>
+ <subtitle><?eval ${docbkxGuideSubTitle}?></subtitle>
<releaseinfo><?eval ${project.version}?></releaseinfo>
@@ -82,3295 +82,882 @@
<!-- main content -->
<chapter id="chp.Intro">
- <title>Introduction</title>
+ <title>Overview</title>
<abstract>
- <para>*** yada yada</para>
+ <para>This chapter gives an overview of the main principles and patterns
+ that underly the Apache Isis approach.</para>
</abstract>
<sect1>
- <title>***</title>
-
- <para><emphasis>*** yada yada</emphasis></para>
- </sect1>
- </chapter>
-
- <chapter id="chp.HowToGuide">
- <title>Developing Domain Objects - A How-To Guide</title>
-
- <abstract>
- <para>Describes a set of conventions for writing domain objects, that
- are together known as the 'Apache Isis (Java) Programming Model'.</para>
- </abstract>
-
- <para>This chapter describes a set of conventions for writing domain
- objects, that are together known as the 'Apache Isis (Java) Programming
- Model'.</para>
-
- <para>Following these conventions does not tie your domain objects to
- Apache Isis or to any other framework: the resulting domain objects may be
- run within any framework or platform that supports POJOs (Plain Old Java
- Objects). A few of the conventions do make use of Interfaces or
- Annotations that are necessarily defined somewhere - in this case they are
- in the application library (AppLib). However, the AppLib is not itself
- dependent upon the rest of the framework (and in theory another framework
- implementation could support the same applib).</para>
-
- <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 <literal>@Bounded</literal> 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 <literal>Country</literal>. This
- is an indication to any interested framework 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 <literal>@DropDownList</literal> because the user
- interface might not support drop-down-lists - but it might provide a
- capability to select from an <literal>@Bounded</literal> class by typing
- the initial letters of the desired instance.</para>
-
- <section>
- <title>Object-level specifications</title>
-
- <para>The first set of conventions are concerned with the capabilities
- or behaviour of an object as a whole.</para>
-
- <section>
- <title>How to specify a title for an object</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. By default, the framework will
- use the object's <literal moreinfo="none">toString()</literal> method
- as the title. However, if a <literal moreinfo="none">title()
- </literal>method (returning a <literal
- moreinfo="none">String</literal>) is present, then that is used
- instead, thus:</para>
-
- <programlisting format="linespecific">public String toString()</programlisting>
-
- <para>or</para>
-
- <programlisting format="linespecific">public String title()</programlisting>
-
- <para>The reason for providing the option to use a <literal
- moreinfo="none">title</literal> method is in case the programmer needs
- to make use of the <literal moreinfo="none">toString</literal> method
- for other purposes, such as for debugging.</para>
- </section>
-
- <section>
- <title>How to specify the icon for an object</title>
-
- <para>By default, the framework will look for an image in the
- <filename class="directory" moreinfo="none">images</filename>
- directory (which must be on the classpath) that has the same name as
- the object class. So for an object of type
- <classname>Customer</classname>, it will look for <filename
- class="directory" moreinfo="none">Customer.gif</filename> or <filename
- class="directory" moreinfo="none">Customer.png</filename>. If it 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</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 'camel case' as the
- convention for icon file names: if you have a class called
- <literal>OrderLine</literal>, then call the icon <filename
- class="directory" moreinfo="none">OrderLine.gif</filename> or
- <filename class="directory" moreinfo="none">OrderLine.png</filename>
- (JPEG suffixes <filename moreinfo="none">OrderLine.jpg</filename> or
- <filename moreinfo="none">OrderLine.jpeg</filename> will also be
- recognized but generally GIF or PNG are to be preferred). Actually,
- the framework will also recognise <filename class="directory"
- moreinfo="none">orderline.gif</filename>, but some operating systems
- and deployment environments are case sensitive, so it is good practice
- to adopt an unambiguous convention.</para>
-
- <para>The programmer may choose to specify, manually, which icon to
- use, by specifying an <literal moreinfo="none">iconName</literal>
- method:</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>
-
- <para>The <literal>iconName</literal> method may also be used to
- specify an individual icon for each instance. For example, an instance
- of <literal>Product</literal> could use a photograph of the product as
- an icon, using:</para>
-
- <programlisting format="linespecific">public String iconName() {
- return getProductName() + "-photograph";
-}</programlisting>
-
- <para>or to vary the icon according to the status of the
- object:</para>
-
- <programlisting format="linespecific"> public String iconName() {
- return "Order-" + getStatus();
- }</programlisting>
- </section>
-
- <section>
- <title>How to specify a name and/or description for an object</title>
-
- <para>By default, the name (or type) of an object, as displayed to the
- user will be the class name. However, if an <literal
- moreinfo="none">@Named</literal> annotation is included, then this
- will override the default name. This might be used to include
- punctuation or other characters that may not be used within a class
- name.</para>
-
- <para>By default the framework will create a plural version of the
- object name by adding an 's' to singular name, or a 'ies' to names
- ending 'y'. For irregular nouns or other special case, the
- <literal>@Plural</literal> annotation may be used to specify the
- plural form of the name explicitly.</para>
-
- <para>(Note that there is an entirely separate mechanism for dealing
- with Internationalisation, which is described elsewhere).</para>
-
- <para>The programmer may optionally also provide a <literal
- moreinfo="none">@DescribedAs</literal> annotations, containing a brief
- description of the object's purpose, from a user perspective. The
- framework will make this available to the user in a form appropriate
- to the user interface style - for example as 'balloon' help.</para>
- </section>
-
- <section>
- <title>How to specify that an object should not be persisted</title>
-
- <para>Use the <literal moreinfo="none">@NotPersistable</literal>
- annotation.</para>
- </section>
-
- <section>
- <title>How to specify that an object should never be modified by the
- user</title>
-
- <para>Use the <literal moreinfo="none">@Immutable</literal>
- annotation.</para>
- </section>
-
- <section>
- <title>How to specify that a class of objects has a limited number of
- instances</title>
-
- <para>Use the <literal moreinfo="none">@Bounded</literal> annotation.
- A common way of describing this is that the whole (limited) set of
- instances may be rendered to the user as a drop down list - but the
- actual interpretation will depend upon the form of the user
- interface.</para>
- </section>
-
- <section>
- <title>How to specify that an object should always be hidden from the
- user</title>
-
- <para>Use the <literal moreinfo="none">@Hidden</literal>
- annotation.</para>
- </section>
- </section>
-
- <section>
- <title>The object lifecycle</title>
-
- <para>These conventions are concerned with the creation, retrieval,
- updating and deletion of objects.</para>
-
- <section>
- <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. 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 intermediate. The application library
- provides an interface:</para>
-
- <para><literal
- moreinfo="none">org.apache.isis.applib.DomainObjectContainer</literal>
- which in turn defines the following methods for managing domain
- objects:</para>
-
- <para><literal moreinfo="none"><T> T newTransientInstance(final
- Class<T> ofClass)</literal> returns a new instance of the
- specified class, that is transient (unsaved). This may subsequently
- 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">void makePersistent(Object
- transientObject)</literal></para>
-
- <para><literal moreinfo="none"><T> T newPersistentInstance(final
- Class<T> ofClass)</literal> creates a new object already
- persisted.</para>
-
- <para><literal moreinfo="none">boolean isPersistent()</literal>checks
- whether an object has already been persisted. (Useful in controlling
- visibility or availability of properties or actions).</para>
-
- <para><literal moreinfo="none">void remove(Object object)</literal>
- deletes a persistent object from the object store.</para>
-
- <para>Any framework that recognises the Isis Programming Model will
- provide an implementation of <literal>DomainObjectContainer</literal>
- and will take responsibility to inject a reference to that Container
- into any domain object that needs it.</para>
-
- <para>A domain object specifies that it needs to have a reference to
- the Container injected into by including the following code:</para>
-
- <programlisting format="linespecific"> private DomainObjectContainer container;
-
- protected DomainObjectContainer getContainer() {
- return this.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 Customer object within another method:</para>
-
- <programlisting format="linespecific"> Customer newCust = (Customer) 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 = (Customer) newTransientInstance(Customer.class);
- newCust.setName("Charlie");
- persist(newCust);</programlisting>
-
- <para>These methods are actually provided by the
- <literal>org.apache.isis.applib.AbstractContainedObject</literal> 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>
-
- <note>
- <para>It is possible to create a transient object within another
- transient object. When the framework persists any transient object,
- 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, 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>
- </note>
- </section>
-
- <section>
- <title>How to insert behaviour into the object life cycle</title>
-
- <para>The following is a list of methods that correspond to various
- events in the life-cycle of a domain object. If a domain object
- implements any of these methods (they are all optional) then the
- framework will call that method whenever the corresponding event
- occurs. For example, <literal
- moreinfo="none"><literal>persisted</literal></literal> is called after
- an object has been persisted. One reason for implementing the <literal
- moreinfo="none"><literal>persisted</literal></literal> method might be
- to set up a reverse association that we do not want to be set up until
- the new object has been persisted.</para>
-
- <para><literal moreinfo="none">public void created() </literal>will be
- called by the framework at logical creation of the object</para>
-
- <para><literal moreinfo="none">public void loading()</literal> will be
- called by the framework when a persistent object is about to be loaded
- into memory</para>
-
- <para><literal moreinfo="none">public void loaded()</literal> will be
- called by the framework when a persistent object has just been loaded
- into memory</para>
-
- <para><literal moreinfo="none">public void persisting() </literal>will
- be called by the framework just before a transient object is first
- persisted. (For backwards compatibility <literal>saving()</literal> is
- also supported).</para>
-
- <para><literal moreinfo="none">public void persisted() </literal> will
- be called by the framework just after a transient object is first
- persisted. (For backwards compatibility <literal>saved()</literal> is
- also supported).</para>
-
- <para><literal moreinfo="none">public void updating() </literal>will
- be called by the framework after any property on a persistent object
- has been changed and just before this change is persisted</para>
-
- <para><literal moreinfo="none">public void updated() </literal>will be
- called by the framework just after a changed property on a persistent
- object has been persisted</para>
-
- <para><literal moreinfo="none">public void removing()</literal> will
- be called by the framework when a persistent object is just about to
- be deleted from the persistent object store. (For backwards
- compatibility <literal>deleting()</literal> is also supported).</para>
-
- <para><literal moreinfo="none">public void removed()</literal> will be
- called by the framework when a persistent object has just been deleted
- from the persistent object store. (For backwards compatibility
- <literal>deleted()</literal> is also supported).</para>
- </section>
- </section>
-
- <section>
- <title>Properties</title>
-
- <para>The following conventions are concerned with specifying the
- properties of an object, and the way in which users can interact with
- those properties.</para>
-
- <section>
- <title>How to add a property to an object</title>
-
- <para>Properties are specified using the JavaBean conventions,
- recognizing a standard accessor/mutator pair (<literal>get</literal>
- and <literal>set</literal>). The syntax is:</para>
-
- <programlisting format="linespecific">public <property type> get<PropertyName>()
-
-public void set<PropertyName>(<property type> param)</programlisting>
-
- <para>where <literal moreinfo="none"><property type></literal>
- is a primitive, a value object or an entity object.</para>
-
- <para>Properties may reference either a value or another domain
- object. Values include Java primitives, and JDK classes with value
- semantics (<literal moreinfo="none">java.lang.Strings</literal> and
- <literal moreinfo="none">java.util.Dates</literal>). 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>
- </section>
-
- <section>
- <title>How to prevent the user from modifying a property</title>
-
- <para>Preventing the user from modifying a property value is known as
- 'disabling' the property.</para>
-
- <section>
- <title>Disabling a property always</title>
-
- <para>Use the <literal moreinfo="none">@Disabled</literal>
- annotation.</para>
- </section>
-
- <section>
- <title>Disabling a property under certain conditions</title>
-
- <para>A <literal moreinfo="none">disable</literal> method can be
- used to disable a particular instance's member under certain
- conditions. The syntax is:</para>
-
- <para><literal moreinfo="none">public String
- disable<PropertyName>()</literal> A non-null return value
- indicates the reason why the property cannot be modified. The
- framework is responsible for providing this feedback to the user.
- For example:</para>
-
- <programlisting format="linespecific">public class OrderLine {
- public String getQuantity() { ... }
- public void setQuantity(int quantity) { ... }
-
- public String disableFirstName() {
- if (this.hasBeenSubmitted()) {
- return "Cannot alter any quantity after Order has been submitted";
- }
- return null;
- }
-}</programlisting>
- </section>
-
- <section>
- <title>Disabling a property for specific users or roles</title>
-
- <para>Generally it is not good practice to embed knowledge of roles
- and/or users into the domain classes. This is the responsibility of
- the framework or platform and should be specified and administered
- externally to the domain model.</para>
-
- <para>However, in rare circumstances it might be necessary or
- pragmatic to implement access control within the domain model using
- the inherited <literal>getUser()</literal> method:</para>
-
- <para>For example:</para>
-
- <programlisting format="linespecific">import org.apache.isis.applib.UserMemento;
-
-public class Employee {
- public BigDecimal getSalary() { ... }
- public void setSalary(BigDecimal salary) { ... }
-
- public String validateSalary(BigDecimal salary) {
- return salary.doubleValue() >= 30000 &&
- !getUser().hasRole("MODIFY_SALARY")?
- "Need MODIFY_SALARY role to increase salary above 30000": null;
- }
-}</programlisting>
- </section>
- </section>
-
- <section>
- <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. To specify that a particular property is
- optional, use the <literal moreinfo="none">@Optional</literal>
- annotation.</para>
- </section>
-
- <section>
- <title>How to specify the size of String properties</title>
-
- <para>Use the <literal moreinfo="none">@MaxLength</literal>, <literal
- moreinfo="none">@TypicalLength</literal> and <literal
- moreinfo="none">@MultiLine</literal> annotations.</para>
- </section>
-
- <section>
- <title>How to validate user input to a property</title>
-
- <para>A <literal moreinfo="none">validate</literal> method is used to
- check that a new value for a property is valid. If the proffered value
- is deemed to be invalid then the property will not be changed. A
- non-null return <literal>String</literal> 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>
-
- <programlisting format="linespecific">public String validate<PropertyName>(<property type> param)</programlisting>
-
- <para>For example:</para>
-
- <programlisting format="linespecific">public class Exam {
- public int getMark() { ... }
- public void setMark(int mark) { ... }
- public validateMark(int mark) {
- return !(mark >= 0 && mark <= 30)?
- "Mark must be in range 0 to 30"
- :null;
- }
-}</programlisting>
-
- <section>
- <title>Format validation</title>
-
- <para>For properties that accept a text input string, such as
- <literal>String</literal> and <literal>Date</literal>, there are
- convenient mechanisms to validate and/or normalise the values typed
- in.</para>
-
- <para>For <literal moreinfo="none">Date</literal> and number values
- the <literal moreinfo="none">@Mask</literal> annotation may be
- used.</para>
-
- <para>For <literal moreinfo="none">String</literal> properties the
- <literal moreinfo="none">@RegEx</literal> annotation may be
- used.</para>
- </section>
- </section>
-
- <section>
- <title>How to specify a set of choices and or a default value 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
- <literal>@Bounded</literal>. However, this is not always appropriate.
- For example you might wish to provide the user with the choice of all
- the Addresses known for that Customer, with the most recently-used
- address as the default.</para>
-
- <para>The syntax for specifying a default value is:</para>
-
- <programlisting format="linespecific">public <property type> default<PropertyName>()</programlisting>
-
- <para>And for specifying a list of choices is:</para>
-
- <programlisting format="linespecific">public <array or collection of property type> choices<PropertyName>()</programlisting>
-
- <para>The full code for our example above is:</para>
-
- <programlisting format="linespecific">public class Order {
- public Address getShippingAddress() { ... }
- public void setShippingAddress() { ... }
- public Address defaultShippingAddress() {
- return getCustomer().normalAddress();
- }
- public List<Address> choicesShippingAddress() {
- return getCustomer().allActiveAddresses();
- }
-}</programlisting>
- </section>
-
- <section>
- <title>How to set up the initial value of a property
- programmatically</title>
-
- <para>Initial values for properties may be set up programmatically
- within the <literal moreinfo="none">created()</literal> method on the
- object. (See Object: Life Cycle).</para>
- </section>
-
- <section>
- <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 <literal
- moreinfo="none">modify</literal> <literal
- moreinfo="none"><propertyName></literal> and include the
- functionality within that. For example:</para>
-
- <programlisting format="linespecific">public int getAmount() {}
-public void setAmount(int amount) {}
-
-public void modifyAmount(int amount) {
- setAmount(amount);
- addToTotal(amount);
-}</programlisting>
-
- <para>The reason for the <literal
- moreinfo="none">modifyAmount</literal> method is that it would not be
- a good idea to include the <literal
- moreinfo="none">addToTotal</literal> call within the <literal
- moreinfo="none">setAmount</literal> method , because that method may
- be called by the persistence mechanism when an object is retrieved
- from storage.</para>
-
- <para>You may optionally also specify a
- <literal>clear<PropertyName></literal> which works the same way
- as modify <literal moreinfo="none">modify</literal> <literal
- moreinfo="none"><propertyName></literal> but is called when the
- property is cleared by the user (i.e. the current value replaced by
- nothing).</para>
-
- <para>These methods may also be used for setting up bidirectional
- relationships (using the 'mutual registration pattern'). For
- example:</para>
-
- <programlisting format="linespecific">public class Employee {
- private Department department;
- public Department getDepartment() {
- return department;
- }
- public void setDepartment(Department department) {
- this.department = department;
- }
- public void modifyDepartment(Department department) {
- setDepartment(department);
- department.addToStaff(this);
- }
- public void clearDepartment(Department department) {
- setDepartment(null);
- department.removeFromStaff(this);
- }
-
- ...
-}</programlisting>
- </section>
-
- <section>
- <title>How to control the order in which properties are
- displayed</title>
-
- <para>Use the <literal moreinfo="none">@MemberOrder</literal>
- annotation.</para>
- </section>
-
- <section>
- <title>How to specify a name and/or description for a property</title>
-
- <section>
- <title>Specifying the name for a property</title>
-
- <para>By default the framework will use the property name itself to
- label the property on the user interface. If you wish to over-ride
- this, use the <literal moreinfo="none">@Named </literal>annotation
- on the property.</para>
- </section>
-
- <section>
- <title>Specifying a description for a property</title>
-
- <para>Use the <literal moreinfo="none">@DescribedAs</literal>
- annotation on the property itself.</para>
-
- <para>The framework will take responsibility to make this
- description available to the user, for example in the form of a
- 'balloon help'.</para>
- </section>
- </section>
-
- <section>
- <title>How to hide a property from the user</title>
-
- <section>
- <title>Hiding a property always</title>
-
- <para>Use the <literal moreinfo="none">@Hidden</literal>
- annotation.</para>
- </section>
-
- <section>
- <title>Hiding a property under certain conditions</title>
-
- <para>A <literal moreinfo="none">hide</literal> method can be used
- to indicate that a particular instance's member should be hidden
- (rendered invisible to the user) under certain conditions. The
- syntax is:</para>
-
- <para><literal moreinfo="none">public boolean
- hide<PropertyName>()</literal> A true return value indicates
- that the property is hidden. For example:</para>
-
- <programlisting format="linespecific">public class Order {
- public String getShippingInstructions() { ... }
- public void setShippingInstructions(String shippingInstructions) { ... }
- public boolean hideShippingInstructions() {
- return hasShipped();
- }
-}</programlisting>
- </section>
-
- <section>
- <title>Hiding a property for specific users or roles</title>
-
- <para><emphasis role="strong">Important</emphasis>: see the comments
- under 'Disabling a property for specific users or roles'.</para>
-
- <para>Session controls provide a way for the class to hide
- properties from specific users, or users not in specific roles. The
- syntax is: <literal moreinfo="none">public boolean
- hide<PropertyName>(Session session)</literal> For
- example:</para>
-
- <programlisting format="linespecific">public class Employee {
- public BigDecimal getSalary() { ... }
- public void setSalary(BigDecimal salary) { ... }
- public boolean hideSalary(UserMemento user, BigDecimal salary) {
- return !user.hasRole("VIEW_SALARY");
- }
-}</programlisting>
- </section>
- </section>
-
- <section>
- <title>How to make a property non-persisted</title>
-
- <para>If there is no mutator (<literal>set</literal>) method then the
- field is not only unmodifiable but will also <emphasis>not</emphasis>
- be persisted. This approach is by design and also happens to be
- compatible with Java Persistence Api (JPA) semantics. This may be used
- to derive a property from other information available to the object,
- for example:</para>
-
- <programlisting format="linespecific">public class Employee {
- public Department getDepartment() { ... }
- public void setDepartment(Department department) { ... }
- public void modifyDepartment(Department department) { ... }
- public void clearDepartment(Department department) { ... }
-
- // this is the derived property
- public Employee getManager() {
- if (getDepartment() == null) { return null; }
- return getDepartment().getManager();
- }
-
- ...
-}</programlisting>
-
- <para>If you need to have a <literal moreinfo="none">get</literal> and
- <literal moreinfo="none">set</literal> method for the property but do
- not wish to have that property persisted, use the <literal
- moreinfo="none">@NotPersisted</literal> annotation.</para>
- </section>
- </section>
-
- <section>
- <title>Actions</title>
-
- <para>An 'action' is a method that we expect the user to be able to
- invoke 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.</para>
-
- <section>
- <title>How to add an action to an object</title>
-
- <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 manual.</para>
-
- <para>If you have a method that you don't want to be made available as
- a user-action you should either make it <literal
- moreinfo="none">protected</literal> or <literal
- moreinfo="none">private</literal> or use the <literal
- moreinfo="none">@Hidden</literal> annotation. Note also that <literal
- moreinfo="none">static</literal> methods are ignored: such
- functionality should reside in a service, such as a Repository or
- Factory.</para>
-
- <para>We refer to all methods that are intended to be invoked by users
- as 'action methods'.</para>
-
- <para>The syntax is:</para>
-
- <programlisting format="linespecific">public void <actionName>([<value or entity type> param]...)</programlisting>
-
- <para>or</para>
-
- <programlisting format="linespecific">public <return type> <actionName>([<value or entity type> param]...)</programlisting>
-
- <para>When a method returns a reference the viewer will attempt to
- display that object. If the return value is <literal>null</literal>
- then nothing is displayed.</para>
- </section>
-
- <section>
- <title>How to specify parameters for an action</title>
-
- <para>If an action method takes parameters, the viewing mechanism will
- automatically require the user to specify these parameters (for
- example, in the form of a dialog box) - and ensure that they are of
- the correct type.</para>
-
- <section>
- <title>Specifying parameter names and/or descriptions</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:) 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. (This is one of
- the few forms of duplication that we cannot eliminate; the parameter
- name is not available in the class' bytecode and so cannot be
- inferred automatically. Alternatively though, you could create a
- user-defined value type, using <literal
- moreinfo="none">@Value</literal>).</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>
- </section>
-
- <section>
- <title>How to make a 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
- over-ride this behaviour by marking up one or more of the paramaters
- with the <literal moreinfo="none">@Optional</literal>
- annotation.</para>
- </section>
-
- <section>
- <title>How to specify default values for parameters</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. The per-parameter form is simpler
- and probably preferred; the syntax is:</para>
-
- <programlisting format="linespecific">public <parameter type> defaultN<ActionName>()</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());
- }
-}</programlisting>
-
- <para>The syntax for specifying all the parameter default values in
- one go is:</para>
-
- <programlisting format="linespecific">public Object[] default<ActionName>([<parameter type> param]...)</programlisting>
-
- <para>returning an array (which must have one element per parameter
- in the action method signature) of corresponding default values. For
- example:</para>
-
- <programlisting format="linespecific">public class Customer {
- public Order placeOrder(
- Product product,
- @Named("Quantity")
- int quantity) {y
- ...
- }
- public Object[] defaultPlaceOrder(
- Product product,
- int quantity) {
- return new Object[] {productMostRecentlyOrderedBy(this.getCustomer()), 1};
- }
-}</programlisting>
- </section>
-
- <section>
- <title>How to specify a set of choices for parameter values</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>As for defaults, there are two different ways to specify
- parameters; either per parameter, or for all parameters. The
- per-parameter form is simpler and probably preferred; the syntax
- is:</para>
-
- <programlisting format="linespecific">public List<parameter type> choicesN<ActionName>()</programlisting>
-
- <para>where N indicates the 0-based parameter number. 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());
- }
-}</programlisting>
-
- <para>The alternative mechanism is to supply all the parameter
- choices in one go:</para>
-
- <programlisting format="linespecific">public Object[] choices<ActionName>([<parameter type> param]...)</programlisting>
-
- <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(), null};
- }
-}</programlisting>
-
- <para>(Note that if the type of the property 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>
- </section>
-
- <section>
- <title>How to specify the length or format for text-input
- parameters</title>
-
- <para>Use the <literal moreinfo="none">@MaxLength</literal>,
- <literal moreinfo="none">@TypicalLength</literal>, <literal
- moreinfo="none">@MultiLine</literal>, <literal
- moreinfo="none">@Mask</literal> or <literal
- moreinfo="none">@RegEx</literal> annotations.</para>
- </section>
-
- <section>
- <title>How to validate parameter values</title>
-
- <para>A <literal moreinfo="none">validate</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. A non-null return String 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>The syntax is:</para>
-
- <programlisting format="linespecific">public String validate<ActionName>([<parameter type> param]...)</programlisting>
-
- <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>
- </section>
- </section>
-
- <section>
- <title>How to specify conditions for invoking an action</title>
-
- <section>
- <title>Disabling an action 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">disable</literal> method.</para>
-
- <para>The syntax is:</para>
-
- <programlisting format="linespecific">public String disable<ActionName>([<parameter type> param]...)</programlisting>
-
- <para>A non-null return <literal>String</literal> indicates the
- reason why the action may not be invoked. The framework takes
- responsibility to provide this feedback to the user. 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>
-
- <para>It is also possible to permanently disable an action using the
- <literal moreinfo="none">@Disabled</literal> annotation. One
- possible reason for doing this might be during prototyping - to
- indicate to the user that an action is planned, but has not yet been
- implemented.</para>
- </section>
-
- <section>
- <title>Disabling an action for certain users or roles</title>
-
- <para>See 'Properties: Disabling a property for specific users or
- roles'. The same technique can be applied to actions. However, the
- caveats apply.</para>
- </section>
- </section>
-
- <section>
- <title>How to control the order in which actions appear on the
- menu</title>
-
- <para>Use the <literal moreinfo="none">@MemberOrder</literal>
- annotation.</para>
- </section>
-
- <section>
- <title>How to hide actions</title>
-
- <section>
- <title>Hiding an action always</title>
-
- <para>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>
- </section>
-
- <section>
- <title>Hiding an action under certain conditions</title>
-
- <para>A <literal moreinfo="none">hide</literal> method can be used
- to indicate that an action should be hidden under certain
- conditions. The syntax is:</para>
-
- <programlisting format="linespecific">public boolean hide<ActionName>([<parameter type> param]...)</programlisting>
-
- <para>A <literal moreinfo="none">true</literal> return value
- indicates that the action should not be shown. For example:</para>
-
- <programlisting format="linespecific">public class Order {
-
- public void applyDiscount(int percentage) { ... }
-
- public boolean hideApplyDiscount() {
- return isWholesaleOrder();
- }
-}</programlisting>
- </section>
-
- <section>
- <title>Hiding an action for certain users or roles</title>
-
- <para>See 'Properties: Hiding a property for specific users or
- roles'. The same technique can be applied to actions. However, the
- caveats apply.</para>
- </section>
- </section>
-
- <section>
- <title>How to pass a message back to the user</title>
-
- <para>Sometimes, within an action it is necessary or desirable to pass
- a message to the user, for example to inform them of the results of
- their action ('5 payments have been issued') or that the action was
- not successful ('No Customer found with name John Smith').
- <literal>DomainObjectContainer</literal> defines two methods for this
- purpose:</para>
-
- <programlisting format="linespecific">void informUser(String message)
-
-void warnUser(String message)</programlisting>
-
- <para>These two methods provide different ways to send a message to
- the user, representing increasing levels of severity. How each of
- these messages is rendered visible to the user is determined by the
- framework.</para>
-
- <para>These two methods are also available on
- <literal>AbstractDomainObject </literal>as a shortcut.</para>
- </section>
- </section>
-
- <section>
- <title>Collections</title>
-
- <para>A collection is a list of references to several entity objects
- that have a common type.</para>
-
- <section>
- <title>How to add a collection to an object</title>
-
- <para>A collection is recognized via an accessor/mutator method pair
- (<literal>get</literal> and set) for any type of collection provided
- by the programming language. The syntax is:</para>
-
- <programlisting format="linespecific">public <collection type> get<CollectionName>()
-
-private void set<CollectionName>(<collection type> param)</programlisting>
-
- <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. (Generics are also required by some persistence
- mechanisms). For example the viewer might display the collection as a
- table, with the columns defined by the visible properties of that
- type. The viewer will then automatically prevent the user from adding
- objects not of that type. If generics are not used then the type may
- be inferred from the <literal moreinfo="none">addTo</literal> /
- <literal moreinfo="none">removeFrom</literal> methods, if specified
- (see below).</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>
- </section>
-
- <section>
- <title>How to trigger other behaviour when an object is added or
- removed</title>
-
- <para>A collection may have a corresponding <literal
- moreinfo="none">addTo</literal> and/or <literal
- moreinfo="none">removeFrom</literal> method. This is equivalent to the
- <literal moreinfo="none">modify</literal> and <literal
- moreinfo="none">clear</literal> methods for properties. The syntax
- is:</para>
-
- <programlisting format="linespecific">public void addTo<CollectionName>(<entity type> param)
-
-public void removeFrom<CollectionName>(<entity type> param)</programlisting>
-
- <para>where <literal moreinfo="none"><entity type></literal> is
- the same type as the generic collection type. 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;
- }
- public void addToEmployees(Employee employee) {
- employees.add(employee);
- }
- public void removeFromEmployees(Employee employee) {
- employees.remove(employee);
- }
-
- ...
-}</programlisting>
-
- <para>The addTo / removeFrom and modify / clear methods are
- particularly useful in setting up bidirectional relationships using
- the 'mutual registration pattern'; for a write-up of this pattern, see
- <ulink
- url="???">http://www.two-sdg.demon.co.uk/curbralan/papers/MutualRegistration.pdf</ulink>.</para>
- </section>
-
- <section>
- <title>How to prevent the user from modifying a collection</title>
-
- <para>Preventing the user from adding to or removing from a collection
- is known as 'disabling' the collection.</para>
-
- <section>
- <title>Disabling a collection permanently</title>
-
- <para>Use the <literal moreinfo="none">@Disabled</literal>
- annotation.</para>
- </section>
-
- <section>
- <title>Disabling a collection under certain conditions</title>
-
- <para>A <literal moreinfo="none">disable</literal> method can be
- used to disable a particular instance's collection under certain
- conditions: The syntax is:</para>
-
- <para><literal moreinfo="none">public String
- disable<CollectionName>()</literal> For example:</para>
-
- <programlisting format="linespecific">public class Department {
- public List<Employee> getEmployees() { ... }
- public void addToEmployees(Employee employee) { ... }
- public void removeFromEmployees(Employee employee) { ... }
- public void disableEmployees() {
- return isClosed()? "This department is closed" : null;
- }
-}</programlisting>
- </section>
-
- <section>
- <title>Disabling a collection for specific users or roles</title>
-
- <para>See Properties: Disabling a property for specific users or
- roles'. The same technique can be applied to collections, with the
- same caveats applying.</para>
- </section>
- </section>
-
- <section>
- <title>How to validate an object to be added or removed</title>
-
- <para>A <literal moreinfo="none">validate</literal> method is used to
- check that an object to be added or removed from a collection is
- valid. A non-null return <literal>String</literal> indicates the
- reason why the object cannot be added/removed, and the viewing
- mechanism will display this to the user. The syntax is:</para>
-
- <programlisting format="linespecific">public String validateAddTo<CollectionName>(<property type> param)
-
-public String validateRemoveFrom<CollectionName>(<property type> param)</programlisting>
-
- <para>For example:</para>
-
- <programlisting format="linespecific">public class Department {
- private List<Employee> employees = new ArrayList<Employee>();
- 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>
- </section>
-
- <section>
- <title>How to control the order in which collections are
- displayed</title>
-
- <para>Use the <literal moreinfo="none">@MemberOrder</literal>
- annotation.</para>
- </section>
-
- <section>
- <title>How to specify a name and/or description for a
- collection</title>
-
- <para>Use the <literal moreinfo="none">@Named</literal> and/or
- <literal moreinfo="none">@DescribedAs</literal> annotations.</para>
- </section>
-
- <section>
- <title>How to hide a collection</title>
-
- <para>See 'Properties: How to hide a property'. The same approaches
- work for collections.</para>
- </section>
-
- <section>
- <title>How to create a derived collection</title>
-
- <para>Collections can be derived, in the same way as properties. These
- are not persisted, but are represented as read only collections. For
- example:</para>
-
- <programlisting format="linespecific">public class Department {
- // Standard collection
- List<Employee> employees = new ArrayList<Employee>();
- public List<Employee> getEmployees() { ... }
- private void setEmployees(List<Employee>) { ... }
- void addToEmployees(final Employee employee) { ... }
- void removeFromEmployees(final Employee employee) { ... }
-
- // Derived collection
- public List<Employee> getTerminatedEmployees() {
- List<Employee> terminatedEmployees = new ArrayListt<Employee>();
- for(Employee e: employees) {
- if (e.isTerminated()) {
- addTo(terminatedEmployees, e);
- }
- }
- return terminatedEmployees;
- }
-}</programlisting>
- </section>
- </section>
-
- <section>
- <title>Repositories and Factories</title>
-
- <para>The Isis AppLib provides a class</para>
-
- <para><literal
- moreinfo="none">org.apache.isis.applib.AbstractFactoryAndRepository</literal></para>
-
- <para>that makes it easy to write a Repository and/or Factory to work
- entirely in memory. This is very useful during prototyping, as you can
- quickly get your system up and running without having to deal with a
- database. Refer to the JavaDoc documentation for that class for a full
- list of methods available. However, the following describes some of the
- most commonly-used methods:</para>
-
- <para><literal moreinfo="none"><T> T newTransientInstance(final
- Class<T> ofClass)</literal> returns a new instance of the
- specified class, that is transient (unsaved). This 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 . . .</para>
-
- <para><literal moreinfo="none">void makePersistent(Object
- transientObject)</literal></para>
-
- <para><literal moreinfo="none"><T> T newPersistentInstance(final
- Class<T> ofClass)</literal> creates a new object already
- persisted.</para>
-
- <para><literal moreinfo="none">void disposeInstance(Object
- persistentObject)</literal></para>
-
- <para><literal moreinfo="none"><T> List<T>
- allInstances(final Class<T> cls, final boolean
- includeSubclasses)</literal> these may be iterated through for a
- specific match.</para>
-
- <para><literal moreinfo="none"><T> T firstMatch(final
- Class<T> cls, final String title, final boolean
- includeSubclasses)</literal>looks for an object with the title
- specified.</para>
-
- <para>There are a number of useful matching and filtering methods in
- AbstractFactoryAndRepository.</para>
-
- <section>
- <title>Finding by pattern</title>
-
- <para><emphasis>TODO: Complete this section to cover pattern-based
- searching, including the fact that this works for both InMemory and
- Hibernate repositories.</emphasis></para>
- </section>
- </section>
-
- <section>
- <title>@MustSatisfy specification</title>
-
- <para>The <literal moreinfo="none">@MustSatisfy</literal> annotation is
- an alternative to using imperative validation, allowing validation rules
- to be captured in an (implementation of a)
- <literal>org.apache.isis.applib.spec.Specification</literal>.</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><emphasis>TODO: @MustSatisfy may not work for action parameters;
- see http://dev.isis.apache.org/trac/ticket/669.</emphasis></para>
- </section>
-
- <section>
- <title>Value Types</title>
-
- <para>In addition to the built-in value types it is also possible to
- define user-defined value types. This is typically done using the
- <literal>@Value</literal> annotation.</para>
-
- <para>The <literal moreinfo="none">@Value</literal> annotation is used
- to provide an implementation of the
- <literal>ValueSemanticsProvider</literal> interface. In turn this
- provides objects that allow the framework to interact with the value, in
- particular for parsing (parsing strings into values and display values
- as strings), and for encoding/decoding (for serialization for
- client/server and used by some persistors).</para>
-
- <para>For more details, explore the built-in types within the applib,
- for example
- <literal>org.apache.isis.applib.value.Money</literal>.</para>
-
- <programlisting format="linespecific">@Value(semanticsProviderName = "org.apache.isis.metamodel.value.MoneyValueSemanticsProvider")
-public class Money extends Magnitude {
- ...
-}</programlisting>
-
- <para>where <literal>MoneyValueSemanticsProvider</literal> is the
- implementation of <literal>ValueSemanticsProvider</literal> described
- above.</para>
-
- <para>Using value types generally removes the need for using <literal
- moreinfo="none">@MustSatisfy</literal> annotation; the rules can instead
- move down into a <literal>validate</literal> method on the value type
- itself.</para>
-
- <para>TODO: need to beef up this discussion. Also, note that it is
- possible to register value types using isis.properties rather than the
- @Value annotation; this is particularly useful for third-party value
- types.</para>
- </section>
-
- <section>
- <title>Resolve and ObjectChanged</title>
-
- <para>In previous versions of the framework it was necessary to call the
- inherited <literal>resolve()</literal> method within every property or
- collection's getter, and <literal>objectChanged()</literal> within every
- property's setter and every collection's addTo or removeFrom.</para>
-
- <para>These methods still exist in
- <literal>AbstractDomainObject</literal>, but no longer need to be called
- explicitly. Instead Isis uses bytecode enhancement (using either cglib
- or javassist) to automatically call these methods. This bytecode
- enhancement can be disabled in <filename
- moreinfo="none">isis.properties</filename> file; if it is then the
- methods must be called manually as they were in earlier versions of the
- framework.</para>
- </section>
- </chapter>
-
- <chapter id="chp.Fixtures">
- <title>Fixtures</title>
-
- <abstract>
- <para>Setup the object store for prototyping or testing</para>
- </abstract>
-
- <para>Fixtures are simple classes that are used to set up the object store
- with sample objects. This is a time-saver when using the in-memory object
- store, either for prototyping or testing, because it means there is always
- a known set of objects to work with. Object stores that actually persist
- data may also support fixtures, though the typical behaviour is to only
- install the fixtures the very first time, ie to seed the object store. On
- subsequent runs the fixture is ignored.</para>
-
- <para>The code for a fixture should be placed in the
- <literal>install</literal> method, which is run when the system installs
- the fixtures. Fixtures are only installed if the
- <literal>ObjectAdapterPersistor</literal> flags that it is uninitialised
- via its <literal>isInitialized</literal> method. For the in memory object
- store this will be every time it is started, and for other object stores
- will only be when they detect they have no persistent data.</para>
-
- <section>
- <title>Registering fixtures</title>
-
- <para>Fixtures are registered with the Framework at startup via the
- properties file, using the fixtures properties. The
- <literal>fixtures</literal> property itself dictates which fixture
- classes are to be instantiated at startup, for example:</para>
-
- <programlisting format="linespecific">isis.fixtures = fixture.BookingsFixture, fixture.PerspectivesFixture</programlisting>
-
- <para>The <literal>prefix</literal> property allows you specify the
- common package name once and, hence omit them from the classes in the
- list. The following set of properties is therefore equivalent to the
- previous example.</para>
-
- <programlisting format="linespecific">isis.fixtures.prefix = fixture
-isis.fixtures = BookingsFixture, PerspectivesFixture</programlisting>
- </section>
- </chapter>
-
- <chapter id="chp.RecognisedAnnotations">
- <title>Recognised Annotations</title>
-
- <abstract>
- <para>Defines the set of annotations that are recognised by the Apache
- Isis (Java) Programming Model.</para>
- </abstract>
-
- <para>This chapter defines the set of annotations that are recognised by
- the Apache Isis (Java) Programming Model.</para>
-
- <section>
- <title>@ActionOrder</title>
-
- <para>Note: The recommended mechanism for specifying the order in which
- actions are listed to the user is <literal>@MemberOrder</literal> (see
- below). <literal>@ActionOrder</literal> provides an alternative
- mechanism, in which the order is specified in one place in the class,
- with the added advantage (currently) that you can easily specify
- groupings (which may be rendered by the viewer as sub-menus). However,
- <literal>@ActionOrder</literal> is more 'brittle' to change: if you
- change the name of an existing action you will need to ensure that the
- corresponding name within the <literal>@ActionOrder</literal> annotation
- is also changed.</para>
-
- <para>The syntax is: <literal moreinfo="none">@ActionOrder("<comma
- separated list of action names>")</literal> (the action names are not
- case sensitive).</para>
-
- <para>For example:</para>
-
- <programlisting format="linespecific"><emphasis role="strong">@ActionOrder("PlaceNewOrder, CheckCredit")</emphasis>
-public class Customer {
-
- public Order placeNewOrder() {}
-
- public CreditRating checkCredit() {}
-
-...
-}</programlisting>
-
- <para>Actions can be grouped together by surrounding the group with
- brackets, and prefixing the group with name and colon. This information
- may be used by the viewing mechanism to render actions into sub-menus.
- For example:</para>
-
- <programlisting format="linespecific"><emphasis role="strong">@ActionOrder("(Account Management: PlaceOrder, CheckCredit), (Personal Details: ChangeOfAddress, AddEmail)")</emphasis>
-public class Customer {
-
- public Order placeNewOrder() {}
-
- public CreditRating checkCredit() {}
-
- public void changeOfAddress() {}
-
- public void addEmail(String emailAddress) {}
-
- ...
-}</programlisting>
- </section>
-
- <section>
- <title>@Bounded</title>
-
- <para>For immutable objects where there is a bounded set of instances,
- the <literal moreinfo="none">@Bounded</literal> annotation can be used.
- For example:</para>
-
- <programlisting format="linespecific"><emphasis role="strong">@Bounded</emphasis>
-public class County {
- // members and actions here
-}</programlisting>
-
- <para>The number of instances is expected to be small enough that all
- instance can be held in memory. The viewer will use this information to
- render all the instances of this class in a drop-down list or
- equivalent. (Note: Although this is not enforced, <literal
- moreinfo="none">@Bounded</literal> is intended for use on <literal
- moreinfo="none">final</literal> classes. Its behaviour when used on
- interfaces, or classes with sub-classes is not specified).</para>
- </section>
-
- <section>
- <title>@Debug</title>
-
- <para>The <literal>@Debug </literal>annotation marks an action method as
- available in debug mode only, and so will not normally be displayed by
- the user interface.</para>
- </section>
-
- <section>
- <title>@DescribedAs</title>
-
- <para>The <literal moreinfo="none">@DescribedAs</literal> annotation is
- used to provide a short description of something that features on the
- user interface. How this description is used will depend upon the
- viewing mechanism - but it may be thought of as being like a 'tool tip'.
- Descriptions may be provided for objects, members (properties,
- collections and actions), and for individual parameters within an action
- method. <literal moreinfo="none">@DescribedAs</literal> therefore works
- in a very similar manner to <literal
- moreinfo="none">@Named</literal>.</para>
-
- <para><emphasis>Providing a description for an object</emphasis></para>
-
- <para>To provide a description for an object, use the <literal
- moreinfo="none">@DescribedAs</literal> annotation immediately before the
- declaration of that object class. For example:</para>
-
- <programlisting format="linespecific"><emphasis role="strong">@DescribedAs("A Customer who may have originally become known to us via " +
- "the marketing system or who may have contacted us directly.")</emphasis>
-public class ProspectiveSale {
- ...
-}</programlisting>
-
- <para><emphasis>Providing a description for a member</emphasis></para>
-
- <para>Any member (property, collection or action) may provide a
- description. To specify this description, use the <literal
- moreinfo="none">@DescribedAs</literal> annotation immediately before the
- declaration of that member. For example:</para>
-
- <programlisting format="linespecific">public class Customer {
- <emphasis role="strong">@DescribedAs("The name that the customer has indicated that they wish to be " +
- "addressed as (e.g. Johnny rather than Jonathan)")</emphasis>
- public String getFirstName() { ... }
-}</programlisting>
-
- <para><emphasis>Providing a description for an action
- parameter</emphasis></para>
-
- <para>To provide a description for an individual action parameter, use
- the <literal moreinfo="none">@DescribedAs</literal> annotation in-line
- i.e. immediately before the parameter declaration. For example:</para>
-
- <programlisting format="linespecific">public class Customer {
- public Order placeOrder(
- Product product,
- @Named("Quantity")
- <emphasis role="strong">@DescribedAs("The quantity of the product being ordered")</emphasis>
- int quantity) {
-
- Order order = new Order();
- order.modifyCustomer(this);
- order.modifyProduct(product);
- order.setQuantity(quantity);
- return order;
- }
- ...
-}</programlisting>
- </section>
-
- <section>
- <title>@Disabled</title>
-
- <para>The <literal moreinfo="none">@Disabled</literal> annotation means
- that the member cannot be used in any instance of the class. When
- applied to the property it means that the user may not modify the value
- of that property (though it may still be modified programmatically).
- When applied to an action method, it means that the user cannot invoke
- that method. For example:</para>
-
- <programlisting format="linespecific">public class Customer {
- <emphasis role="strong">@Disabled</emphasis>
- public void assessCreditWorthiness() { ... }
-
- <emphasis role="strong">@Disabled</emphasis>
- public int getInitialCreditRating(){ ... }
- public void setInitialCreditRating(int initialCreditRating) { ... }
-}</programlisting>
-
- <para>Note that if an action is marked as <literal
- moreinfo="none">@Disabled</literal>, it will be shown on the user
- interface but cannot ever be invoked. One possible reason to do this is
- during prototyping, to indicate an action that is still to be developed.
- If a method is intended for programmatic use, but not intended ever to
- be invoked directly by a user, then it should be marked as <literal
- moreinfo="none">@Hidden</literal> instead.</para>
-
- <para>This annotation can also take a single parameter indicating when
- it is to be hidden, for example the following code would disable the
- action until the object has been saved.</para>
-
- <programlisting format="linespecific">public class Customer {
- <emphasis role="strong">@Disabled</emphasis>(When.UNTIL_PERSISTED)
- public void assessCreditWorthiness() { ... }
-}</programlisting>
-
- <para>The acceptable values for the parameter are:
- <literal>When.ALWAYS</literal>, <literal>When.NEVER</literal>,
- <literal>When.ONCE_PERSISTED</literal> and
- <literal>When.UNTIL_PERSISTED</literal>. By default the annotated
- property or action is always disabled i.e. it is implicitly
- <literal>When.ALWAYS.</literal></para>
- </section>
-
- <section>
- <title>@Executed</title>
-
- <para>The <literal>@Executed</literal> annotation overrides the default
- location where a method is executed.</para>
-
- <para><emphasis>Forcing a method to be executed on the
- client</emphasis></para>
-
- <para>The <literal>@Executed(Where.LOCALLY)</literal> annotation marks
- an action method so that it executes on the client, rather than being
- forwarded to the server for execution. This is useful for methods that
- invoke a service that must be run client-side, for example spawning off
- a separate process (such as a web browser or Acrobat Reader).</para>
-
- <para><emphasis>Forcing a method to be executed on the
- server</emphasis></para>
-
- <para>The <literal>@Executed(Where.REMOTELY)</literal> annotation marks
- an action method so that it executes on the server, even though it would
- normally be executed on the client (as methods for transient objects
- are). This is useful for methods that although based on transient
- objects need access to persistent objects.</para>
- </section>
-
- <section>
- <title>@Exploration</title>
-
- <para>The <literal>@Exploration</literal> annotation marks an action
- method as available in exploration mode only, and therefore not intended
- for use in the production system</para>
- </section>
-
- <section>
- <title>@FieldOrder</title>
-
- <para>Note: The recommended mechanism for specifying the order in which
- fields are listed to the user is <literal>@MemberOrder</literal> (see
- below). <literal>@FieldOrder</literal> provides an alternative
- mechanism, in which the order is specified in one place in the class.
- However, <literal>@FieldOrder</literal> is more 'brittle' to change: if
- you change the name of an existing property you will need to ensure that
- the corresponding name within the <literal>@FieldOrder</literal>
- annotation is also changed.</para>
-
- <para>The syntax is: <literal moreinfo="none">@FieldOrder("<comma
- separated list of field names>") </literal>(the field names are not
- case sensitive.).</para>
-
- <para>For example:</para>
-
- <programlisting format="linespecific"><emphasis role="strong">@FieldOrder("Name, Address, DateOfBirth, RecentOrders")</emphasis>
-public class Customer {
-
- public Date getDateOfBirth() {...}
-
- public List<Order> getRecentOrders() {...}
-
- public String getAddress() {...}
-
- public String getName() {...}
-
- ...
-}</programlisting>
- </section>
-
- <section>
- <title>@Hidden</title>
-
- <para>The <literal moreinfo="none">@Hidden</literal> annotation
- indicates that the member (property, collection or action) to which it
- is applied should never be visible to the user. For example:</para>
-
- <programlisting format="linespecific">public class Customer {
- private int internalId;
-
- <emphasis role="strong">@Hidden</emphasis>
- public int getInternalId() {
- return internalId;
- }
-
- <emphasis role="strong">@Hidden</emphasis>
- public void updateStatus() { ... }
-}</programlisting>
-
- <para>This annotation can also take a single parameter indicating when
- it is to be hidden, for example the following code would show the Id
- until the object has been saved, and then would hide it.</para>
-
- <programlisting format="linespecific">public class Customer {
- private int internalId;
-
- <emphasis role="strong">@Hidden</emphasis>(When.ONCE_PERSISTED)
- public int getInternalId() {
- return internalId;
- }
-}</programlisting>
-
- <para>The acceptable values for the parameter are:
- <literal>When.ALWAYS</literal>, <literal>When.NEVER</literal>,
- <literal>When.ONCE_PERSISTED</literal> and
- <literal>When.UNTIL_PERSISTED</literal>. By default the annotated
- property or action is always hidden i.e. it is implicitly
- <literal>When.ALWAYS.</literal></para>
- </section>
-
- <section>
- <title>@Immutable</title>
-
- <para>The <literal moreinfo="none">@Immutable</literal> annotation may
- be applied to a class. The framework does not allow the state of such
- objects to be changed through the UI, and it should be considered a
- programmer error to do so programmatically. The ObjectStorePersistor, as
- used to run the in-memory and Hibernate object stores will actually fail
- if the programmer tries to change an object in a way that cause the
- persistor to try and save it. For example the following class would
- prevent the user from changing the object even when transient:</para>
-
- <programlisting format="linespecific"><emphasis role="strong">@Immutable</emphasis>
-public class Country {
- ...
-}</programlisting>
-
- <para>This annotation can also take a single parameter indicating when
- it is to become immutable, for example the following code would allow
- the user to create an email object and set it up, and then prevent any
- changes once it has been saved.</para>
-
- <programlisting format="linespecific"><emphasis role="strong">@Immutable</emphasis>(When.ONCE_PERSISTED)
-public class Email {
- ...
-}</programlisting>
-
- <para>The acceptable values for the parameter are:
- <literal>When.ALWAYS</literal>, <literal>When.NEVER</literal>,
- <literal>When.ONCE_PERSISTED</literal> and
- <literal>When.UNTIL_PERSISTED</literal>. By default the annotated
- property or action is always immutable i.e. it is implicitly
- <literal>When.ALWAYS.</literal></para>
- </section>
-
- <section>
- <title id="mask-annotation">@Mask</title>
-
- <para>The <literal moreinfo="none">@Mask </literal>annotation may be
- applied to any property, or to any parameter within an action method,
- that allows the user to type in text as input. The mask serves to
- validate, and potentially to normalise, the format of the input. The
- characters that can be used are based on Swing's MaskFormatter, and also
- Java's SimpleDateFormat. When applying a mask to a value property, the
- annotation should be applied to the 'getter'. For example:</para>
-
- <programlisting format="linespecific">public class Email {
- private String telNo;
-
- <emphasis role="strong">@Mask("(NNN)NNN-NNNN")</emphasis>
- public String getTelephoneNumber() {...}
-
- public void setTelephoneNumber(String telNo) {...}
- ...
-}</programlisting>
-
- <para>When applying a mask to a value parameter within an action method,
- the annotation should be applied 'in-line' before that parameter). For
- example:</para>
-
- <programlisting format="linespecific">public void newContact(
- @Named("Contact Name")
- String contactName,
- @Named("Telephone Number")
- <emphasis role="strong">@Mask("(NNN)NNN-NNNN")</emphasis>
- String telNo) {}</programlisting>
- </section>
-
- <section>
- <title>@MaxLength</title>
-
- <para>The <literal moreinfo="none">@MaxLength</literal> annotation
- indicates the maximum number of characters that the user may enter into
- a <literal moreinfo="none">String</literal> property, or a <literal
- moreinfo="none">String</literal> parameter in an action. (It is ignored
- if applied to a property or parameter of any other type.) For
- example:</para>
-
- <programlisting format="linespecific">public class Customer {
-
- <emphasis role="strong">@MaxLength(30)</emphasis>
- public String getFirstName() { ... }
- public void setFirstName(String firstName) { ... }
- ...
-}</programlisting>
-
- <para>If the model is being persisted on a relational database then
- <literal moreinfo="none">@MaxLength</literal> should be specified for
- all <literal moreinfo="none">String</literal> properties and action
- parameters.</para>
- </section>
-
- <section>
- <title>@MemberOrder</title>
-
- <para><literal>@MemberOrder</literal> is the recommended mechanism for
- specifying the order in which fields and/or actions are presented to the
- user. (<literal>@ActionOrder</literal> and
- <literal>@FieldOrder</literal> provide alternative mechanisms).</para>
-
- <para><literal>@MemberOrder</literal> is specified at the individual
- member level, on a 'relative' basis. The syntax is:</para>
-
- <programlisting format="linespecific">@MemberOrder(sequence = "<relative order>")</programlisting>
-
- <para>where <literal><relative order></literal> may be any string.
- The actual sequence is determined by comparing all the values of the
- sequence specifier string, using the standard <literal>String</literal>
- comparator.</para>
-
- <para>The simplest convention is to use numbers - 1, 2, 3 - though it is
- a better idea to leave gaps in the numbers - 10, 20, 30 perhaps - such
- that a new member may be added without having to edit existing numbers.
- A useful alternative is to adopt the 'dot-decimal' notation - 1, 1.1,
- 1.2, 2, 3, 5.1.1, 5.2.2, 5.2, 5.3 - which allows for an indefinite
- amount of future insertion. For example:</para>
-
- <programlisting format="linespecific">Public Class Customer {
- @MemberOrder(sequence="2.1")
- Public String getAddress() {...}
- Public void setAddress(value as String) {...}
-
- @MemberOrder(sequence="1.1")
- Public String getFirstName() {...}
- Public void setFirstName(value as String) {...}
-
- @MemberOrder(sequence="1.2")
- Public String getLastName() {...}
- Public void setLastName(value as String) {...}
-
- @MemberOrder(sequence="3")
- Public Date getDateOfBirth() {...}
- Public void setDateOfBirth(value as Date) {...}
- ...
-} </programlisting>
-
- <para>If a member does not have a specified order then it will be placed
- after those that are specified. Two members may have the same sequence
- specifier, but in such a case the relative ordering of those members
- will be indeterminate.</para>
-
- <para>This approach is especially useful when dealing with inheritance
- hierarchies, as it allows sub-classes to specify where their additional
- members should be placed in relation to those inherited from the
- super-class.</para>
-
- <para>Note that certain styles of user interface will lay out an
- object's properties and its collections separately, in which case the
- relative member order of properties and collections will be evaluated
- separately. However, since other styles of user interface may interleave
- properties and collections, it is safer to assume the latter.</para>
- </section>
-
- <section>
- <title>@MultiLine</title>
-
- <para>The <literal moreinfo="none">@MultiLine</literal> annotation
- provides information about the carriage returns in a <literal
- moreinfo="none">String</literal> property or action parameter. The
- annotation indicates that:</para>
-
- <para>- the <literal>String</literal> property or parameter may contain
- carriage returns, and</para>
-
- <para>- (optionally) the typical number of such carriage returns,
- and</para>
-
- <para>- (optionally) that the text should be wrapped (the default is
- that text is not wrapped).</para>
-
- <note>
- <para>Currently the <literal moreinfo="none">preventWrapping</literal>
- functionality is not fully implemented.</para>
- </note>
-
- <para>This may be used by the viewing mechanism to render the property
- as a multi-line textbox (or text-editor when changes are permitted),
- with appropriate wrapping and/or scrollbars. The syntax is:</para>
-
- <para><literal
- moreinfo="none">@MultiLine([numberOfLines=<typicalNumberOfCRs>]
- [,preventWrapping=<false|true>])</literal></para>
-
- <para>For example:</para>
-
- <programlisting format="linespecific">public class BugReport {
- <emphasis role="strong">@MultiLine(numberOfLines=10)</emphasis>
- public String getStepsToReproduce() { ... }
- public void setStepsToReproduce(String stepsToReproduce) { ... }
- ...
-}</programlisting>
-
- <para>Here the <literal moreinfo="none">stepsToReproduce</literal> may
- be displayed in a text area of 10 rows, with no wrapping. A horizontal
- scrollbar may appear if the number of characters on any given row
- exceeds the width. Another example:</para>
-
- <programlisting format="linespecific">public class Email {
- <emphasis role="strong">@MultiLine(numberOfLines=20, preventWrapping=false)</emphasis>
- public String getBody() { ... }
- public void setBody(String body) { ... }
- ...
-}</programlisting>
-
- <para>Here the body should be displayed in a text area of 20 rows, with
- wrapping. If this attribute is combined with the <literal
- moreinfo="none"><TypicalLength></literal>, then the expected width
- of the text area in the user interface will be determined by the value
- of the typical length divided by the number of specified lines. For
- example:</para>
-
- <programlisting format="linespecific">public class Email {
- <emphasis role="strong">@MultiLine(numberOfLines=20, preventWrapping=false)</emphasis>
- @TypicalLength(800)
- public String getBody() { ... }
- public void setBody(String body) { ... }
- ...
-}</programlisting>
-
- <para>Here the body will (likely be) displayed in a text area of 20
- rows, with 40 columns.</para>
- </section>
-
- <section>
- <title>@MustSatisfy</title>
-
- <para>The <literal moreinfo="none">@MustSatisfy</literal> annotation
- allows validation to be applied to properties using an (implementation
- of a) <literal>org.apache.isis.applib.spec.Specification</literal>
- object.</para>
-
- <para>For example:</para>
-
- <programlisting format="linespecific">public class Customer {
- <emphasis role="strong">@MustSatisfy(StartWithCapitalLetterSpecification.class)</emphasis>
- public String getFirstName() { ... }
-
- ...
-}</programlisting>
-
- <para>The <literal>Specification</literal> is consulted during
- validation, being passed the proposed value.</para>
- </section>
-
- <section>
[... 1907 lines stripped ...]