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 2011/04/23 17:48:16 UTC
svn commit: r1096164 [3/4] - in /incubator/isis/trunk:
applib/src/main/java/org/apache/isis/applib/adapters/
core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetdecorator/
core/progmodel/ core/progmodel/src/main/java/org/apache/isis/core/p...
Modified: incubator/isis/trunk/core/src/docbkx/guide/isis-core.xml
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/core/src/docbkx/guide/isis-core.xml?rev=1096164&r1=1096163&r2=1096164&view=diff
==============================================================================
--- incubator/isis/trunk/core/src/docbkx/guide/isis-core.xml (original)
+++ incubator/isis/trunk/core/src/docbkx/guide/isis-core.xml Sat Apr 23 15:48:14 2011
@@ -71,7 +71,7 @@
<para>This guide is written for programmers looking to understand how
the core framework of <emphasis>Apache Isis</emphasis> fits together,
including an understanding of its core <acronym>API</acronym>s. It is
- divided into the following parts/chapters:<itemizedlist>
+ divided into the following chapters:<itemizedlist>
<listitem>
<para>Architectural Overview</para>
@@ -308,8 +308,8 @@
</listitem>
</itemizedlist>
- <para>These modules are covered more extensively in <xref
- linkend="prt.CoreModules" />.</para>
+ <para>These modules are covered more extensively in the following
+ chapters.</para>
</sect1>
<sect1>
@@ -379,8 +379,9 @@
</listitem>
</itemizedlist>
- <para>The intent is that other runtimes implementations (eg using
- CDI, JDO 3.0 etc) will be supported in the future.</para>
+ <para>At the time of writing no other runtimes are currently
+ implemented, but the intention is that other runtimes (eg using CDI,
+ JDO 3.0 etc) will be supported in the future.</para>
</listitem>
</itemizedlist>
</sect1>
@@ -478,31 +479,19 @@
</sect1>
</chapter>
- <part id="prt.CoreModules">
- <title>Core Framework Modules</title>
+ <chapter>
+ <title><emphasis>Test Support</emphasis> Module</title>
- <partintro>
- <abstract>
- <para>The modules that make up the core framework.</para>
- </abstract>
-
- <para>This part of the guide goes through each of the modules that make
- up the core framework.</para>
- </partintro>
-
- <chapter>
- <title><emphasis>Test Support</emphasis> Module</title>
-
- <abstract>
- <para>Classes and interfaces in the
- <package>oai.core.testsupport</package> module.</para>
- </abstract>
-
- <para>The <emphasis>testsupport</emphasis> module holds helper classes
- to support writing unit tests using either JUnit or JMock. It should
- only ever be added as a dependency with a scope of test:</para>
+ <abstract>
+ <para>Classes and interfaces in the
+ <package>oai.core.testsupport</package> module.</para>
+ </abstract>
- <para><programlisting><dependencies>
+ <para>The <emphasis>testsupport</emphasis> module holds helper classes to
+ support writing unit tests using either JUnit or JMock. It should only
+ ever be added as a dependency with a scope of test:</para>
+
+ <para><programlisting><dependencies>
<dependency>
<groupId>org.apache.isis.core</groupId
<artifactId>testsupport</artifactId>
@@ -512,1321 +501,1299 @@
...
</dependencies></programlisting></para>
- <sect1>
- <title>JMock Support</title>
+ <sect1>
+ <title>JMock Support</title>
- <para>The classes in the <package>oai.core.testsupport.jmock</package>
- package provide convenience adapters for <ulink
- url="http://jmock.org">JMock</ulink>. For example, they provide the
- <classname>MockFixture</classname> interface that allows mocks
- expectations to be managed as fixture objects in their own right
- (thereby making such expectations reusable across tests). They also
- provide subclasses of the <package>org.jmock.Mockery</package> class
- with a number of convenience methods.</para>
- </sect1>
-
- <sect1>
- <title>JUnit Support</title>
-
- <para>The classes in the
- <classname>oai.core.testsupport.junit</classname> package provide
- helper classes designed, among other things, to make it easier to
- write value types.</para>
- </sect1>
- </chapter>
-
- <chapter>
- <title><emphasis>Commons</emphasis> Module</title>
-
- <abstract>
- <para>Classes and interfaces in the
- <package>oai.core.commons</package> module.</para>
- </abstract>
-
- <para>The <emphasis>core commons</emphasis> module provides a set of
- common utilities for use across the rest of the framework. It also
- defines a number of small, mostly internal,
- <acronym>API</acronym>s.</para>
-
- <para>Generally it shouldn't be necessary to add an explicit dependency
- to the <emphasis>commons</emphasis> module, because it will be depended
- upon transitively by other modules in
- <package>oai.core</package>.</para>
-
- <sect1>
- <title>Package Layering / Dependencies</title>
-
- <para>The packages that reside within <emphasis>commons</emphasis>
- have break into the following layers (top layer packages depending on
- lower layers):</para>
+ <para>The classes in the <package>oai.core.testsupport.jmock</package>
+ package provide convenience adapters for <ulink
+ url="http://jmock.org">JMock</ulink>. For example, they provide the
+ <classname>MockFixture</classname> interface that allows mocks
+ expectations to be managed as fixture objects in their own right
+ (thereby making such expectations reusable across tests). They also
+ provide subclasses of the <package>org.jmock.Mockery</package> class
+ with a number of convenience methods.</para>
+ </sect1>
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/common/architecture-perspective.png"
- scale="60" />
- </imageobject>
- </mediaobject>
+ <sect1>
+ <title>JUnit Support</title>
- <para>Alternatively we can see the actual dependencies:</para>
+ <para>The classes in the
+ <classname>oai.core.testsupport.junit</classname> package provide helper
+ classes designed, among other things, to make it easier to write value
+ types.</para>
+ </sect1>
+ </chapter>
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/common/composition-perspective.png"
- scale="60" />
- </imageobject>
- </mediaobject>
+ <chapter>
+ <title><emphasis>Commons</emphasis> Module</title>
- <para>The relatively small number of dependencies between these
- packages shows the extent to which the utility classes in common are
- independent of each other.</para>
- </sect1>
-
- <sect1>
- <title>APIs</title>
-
- <sect2>
- <title><classname>Component</classname> and
- <classname>Installer</classname> <acronym>API</acronym></title>
-
- <para><emphasis>Isis</emphasis> is a modular framework, and the
- <classname>Component</classname> interface (in
- <package>oai.core.commons.components</package> package) represents
- this abstraction. For example, an authentication manager is a
- <classname>Component</classname>, and so too is an adapter map (for
- tracking object identities).</para>
-
- <para><classname>Component</classname> has three subinterfaces to
- represent different scopes (or lifetimes) of component instances,
- namely:</para>
+ <abstract>
+ <para>Classes and interfaces in the <package>oai.core.commons</package>
+ module.</para>
+ </abstract>
- <itemizedlist>
- <listitem>
- <para><classname>ApplicationScopedComponent</classname>, for
- components that exist for the duration of the application</para>
- </listitem>
+ <para>The <emphasis>core commons</emphasis> module provides a set of
+ common utilities for use across the rest of the framework. It also defines
+ a number of small, mostly internal, <acronym>API</acronym>s.</para>
+
+ <para>Generally it shouldn't be necessary to add an explicit dependency to
+ the <emphasis>commons</emphasis> module, because it will be depended upon
+ transitively by other modules in <package>oai.core</package>.</para>
- <listitem>
- <para><classname>SessionScopedComponent</classname> , for
- components that are created a-new for each session, and</para>
- </listitem>
-
- <listitem>
- <para><classname>TransactionScopedComponent</classname>, for
- components that are bound to a single transaction.</para>
- </listitem>
- </itemizedlist>
+ <sect1>
+ <title>Package Layering / Dependencies</title>
- <para>For webapp/server-based deployments, a session is created for
- each interaction<footnote>
- <para>Just like JPA or Hibernate sessions.</para>
- </footnote>. There is typically just one transaction per
- session.</para>
-
- <para>For client/standalone deployments, the session lasts for the
- duration of the application, and so is one-to-one with the
- application scope. For these cases a transaction is used to wrap
- each client/server interaction<footnote>
- <para>Strictly speaking, this is a statement about how the the
- <emphasis>default runtime</emphasis> implementation works. Other
- runtimes could conceivably take a different approach. If you are
- only intending to use webapp-based viewers, then don't worry
- about it... <emphasis>Isis</emphasis> works similarly to
- JPA/Hibernate.</para>
- </footnote>.</para>
-
- <para>Closely related to <classname>Component</classname> is the
- <classname>Installer</classname> interface, which acts as a
- <classname>Component</classname> factory. Each Installer provides a
- type (a string) and a name (also a string), and the combination of
- (type, name) is expected to be unique. For example, the DnD viewer
- has a type of "viewer" and a name of "dnd".</para>
-
- <para>This (type, name) combination is used to determine the
- configuration files that are searched for when the
- <classname>Component</classname> is created. Each
- <classname>Component</classname>'s <classname>Installer</classname>
- will search for at least two property files:
- <filename>type.properties</filename> and
- <filename>type_name.properties</filename>. For example, the DnD
- viewer will search for both <filename>viewer.properties</filename>
- and also <filename>viewer_dnd.properties</filename> file<footnote>
- <para>In fact, it is possible for an
- <classname>Installer</classname> to nominate additional property
- files; this is sometimes appropriate where a
- <classname>Component</classname> does double-duty and plays more
- than one role. At the time of writing this capability was only
- used by the components that install client/server remoting for
- the <emphasis>default runtime</emphasis> module.</para>
- </footnote>. An Installer can also indicate whether a missing
- config file should be treated as an error or can be ignored
- (generally the latter).</para>
- </sect2>
-
- <sect2 id="sec.ConfigurationApi">
- <title><classname>IsisConfiguration</classname> and
- <classname>IsisConfigurationBuilder</classname>
- <acronym>API</acronym></title>
-
- <para>The <classname>IsisConfigurationBuilder</classname> (in
- <package>oai.core.commons.config</package> package) is used to hold
- the "current" configuration; as (the
- <classname>Installer</classname> for)
- <classname>Component</classname>s are loaded each indicates the
- property file(s) to load, and these are used to update the current
- configuration held within
- <classname>IsisConfigurationBuilder</classname>. The "current"
- configuration is initially just the properties in the
- <filename>isis.properties</filename> file (see <xref
- linkend="sec.ResourceStreamApi" /> for details on where this file is
- actually loaded from) .</para>
-
- <para>When the <classname>Component</classname> is actually
- instantiated, it is handed an immutable
- <classname>IsisConfiguration</classname> that can be thought of as a
- snapshot of the set of properties held by the
- <classname>IsisConfigurationBuilder</classname>. A consequence of
- this design is that different <classname>Component</classname>s will
- have references to different
- <classname>IsisConfiguration</classname> objects; though all should
- always have access to "their" properties.</para>
-
- <para>Using properties specified in the configuration files is done
- by get the <classname>IsisConfiguration</classname> singleton from
- the context and using one of the lookup methods to get a value, as
- the example below shows. The <literal
- moreinfo="none">Configuration.ROOT</literal> constant provides the
- base property name ("isis."). If no value is found with the
- specified property name exists then null (or 0 or false) will be
- returned.</para>
-
- <programlisting format="linespecific">String formatRequired = getConfiguration().getString(Configuration.ROOT + "value.format.date");</programlisting>
- </sect2>
-
- <sect2>
- <title
- id="sec.ResourceStreamApi"><classname>ResourceStreamSource</classname>
- <acronym>API</acronym></title>
-
- <para>The <classname>ResourceStreamSource</classname> interface (in
- <package>oai.core.commons.resource</package> package) is an
- abstraction over locating resource files. It is used predominantly
- to locate configuration files (see <xref
- linkend="sec.ConfigurationApi" />), with implementations to load
- from the config directory or from the classpath.</para>
-
- <para>Different implementations of
- <classname>IsisConfigurationBuilder</classname> use
- <classname>ResourceStreamSource</classname> in order to search for
- config files in specific locations. In principle it would be
- straightforward to write a new implementation of
- <classname>ResourceStreamSource</classname> that loads config files
- from some other location (eg LDAP, the Windows registry or a
- database) and then write a new
- <classname>IsisConfigurationBuilder</classname> to use it.</para>
- </sect2>
-
- <sect2>
- <title>Encoding <acronym>API</acronym></title>
-
- <para>The <package>oai.core.commons.encoding</package> package
- provides a number of classes to support the custom serialization of
- elements of any element:</para>
+ <para>The packages that reside within <emphasis>commons</emphasis> have
+ break into the following layers (top layer packages depending on lower
+ layers):</para>
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/common/encoding-classdiagram.png"
- scale="30" />
- </imageobject>
- </mediaobject>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="images/common/architecture-perspective.png"
+ scale="60" />
+ </imageobject>
+ </mediaobject>
- <para>The <classname>DataInputExtended</classname> and
- <classname>DataOutputExtended</classname> interfaces are
- straightforward extensions of
- <classname>java.io.DataInput</classname> and
- <classname>java.io.DataOutput</classname> respectively, simply
- adding the capability to serialize arrays of primitives. The
- <classname>DataInputStreamExtended</classname> and
- <classname>DataOutputStreamExtended</classname> implement these
- interfaces, providing the ability to read from/write to an
- underlying <classname>java.io.InputStream</classname>.</para>
-
- <para>Finally, the <classname>Encodable</classname> interface
- defines a contract for objects to write themselves to a
- <classname>DataOutputStreamExtended</classname>, with an implied
- contract that they can be re-constructed from a corresponding
- <classname>DataInputStreamExtended</classname>.</para>
-
- <para>The primary usage of the encoding API is to enable
- client/server remoting, as supported by the <emphasis>default
- runtime</emphasis> implementation. However, it is also used in order
- to create <classname>Memento</classname>s of domain objects (again,
- a capability of the <emphasis>default runtime</emphasis>). This is
- used by some viewers in order to maintain a handle on transient
- (not-yet-persisted) objects.</para>
- </sect2>
-
- <sect2>
- <title><classname>AuthenticationSession</classname>
- Definition</title>
-
- <para>The <classname>AuthenticationSession</classname> interface (in
- the <package>oai.core.commons.authentication</package> package)
- provides a representation of an authenticated user within the
- system.</para>
-
- <para>Also worth mentioning is the utility class
- <classname>AuthenticationSessionUtils</classname> can be used to
- create an <classname>oai.applib.security.UserMemento</classname>,
- which is the corresponding type within the applib (that is, the
- identity of the authenticated user as the domain objects understand
- it).</para>
-
- <para>The interface to actually authenticate users and create
- <classname>AuthenticationSession</classname>s - namely
- <classname>AuthenticationManager</classname> - is defined in the
- <emphasis>core runtime</emphasis> module (see <xref
- linkend="chp.Runtime" />). This shouldn't be confused with
- <classname>AuthenticationSessionProvider</classname> which merely
- returns the current <classname>AuthenticationSession</classname>
- <emphasis>if one exists</emphasis>.</para>
- </sect2>
-
- <sect2>
- <title><classname>Debuggable</classname>
- <acronym>API</acronym></title>
-
- <para>The <classname>Debuggable</classname> interface(in the
- <classname>oai.core.commons.debug</classname> package) is used by
- some <classname>Component</classname>s in order to build structured
- string representations of themselves for debug purposes. A good
- example is the debug menu options available within the DnD
- viewer.</para>
- </sect2>
-
- <sect2>
- <title>Hamcrest <classname>Matcher</classname>s</title>
-
- <para>The <classname>IsisMatchers</classname> class (in the
- <classname>oai.core.commons.matchers</classname> package) provides a
- collection of <ulink url="http://hamcrest.org">Hamcrest
- </ulink><classname>Matcher</classname>s for use in both tests and
- also production code.</para>
- </sect2>
-
- <sect2>
- <title><classname>Ensure</classname> API</title>
-
- <para>The <classname>Ensure</classname> class (in the
- <package>oai.core.commons.ensure</package> package) allows
- assertions to be made about arguments, state or general context, and
- uses Hamcrest <classname>Matcher</classname>s to express those
- assertions.</para>
- </sect2>
- </sect1>
- </chapter>
-
- <chapter id="chp.MetaModel">
- <title><emphasis>Metamodel</emphasis> Module</title>
-
- <abstract>
- <para>Classes and interfaces in the
- <package>oai.core.metamodel</package> module.</para>
- </abstract>
-
- <para>The core <emphasis>metamodel</emphasis> module defines the
- interfaces and classes that make up the <emphasis>Apache Isis</emphasis>
- metamodel. This metamodel is at the very heart of
- <emphasis>Isis</emphasis>, and used in numerous ways:</para>
+ <para>Alternatively we can see the actual dependencies:</para>
- <itemizedlist>
- <listitem>
- <para>by viewers to obtain information about the domain objects, so
- that they can be rendered in a generic object-oriented user
- interface;</para>
- </listitem>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="images/common/composition-perspective.png"
+ scale="60" />
+ </imageobject>
+ </mediaobject>
- <listitem>
- <para>by persistence mechanisms (within the <emphasis>default
- runtime</emphasis> module, <package>(oai.runtimes:dflt)</package>)
- to determine which data is to be persisted;</para>
- </listitem>
+ <para>The relatively small number of dependencies between these packages
+ shows the extent to which the utility classes in common are independent
+ of each other.</para>
+ </sect1>
- <listitem>
- <para>by client/server remoting (within the <emphasis>default
- runtime</emphasis> module) , to marshall domain objects
- automatically between different tiers;</para>
- </listitem>
+ <sect1>
+ <title>APIs</title>
- <listitem>
- <para>to provide the ability to provide XML Snapshots (through the
- <classname>XmlSnapshot</classname> utility class, in the
- <emphasis>core runtime</emphasis> module,
- <package>(oai.core:runtime)</package>).</para>
- </listitem>
- </itemizedlist>
+ <sect2 id="sec.ComponentAndInstallerApi">
+ <title><classname>Component</classname> and
+ <classname>Installer</classname> <acronym>API</acronym></title>
+
+ <para><emphasis>Isis</emphasis> is a modular framework, and the
+ <classname>Component</classname> interface (in
+ <package>oai.core.commons.components</package> package) represents
+ this abstraction. For example, an authentication manager is a
+ <classname>Component</classname>, and so too is an adapter map (for
+ tracking object identities).</para>
+
+ <para><classname>Component</classname> has three subinterfaces to
+ represent different scopes (or lifetimes) of component instances,
+ namely:</para>
- <para>In addition, the metamodel provides a mechanism for the framework
- and the clients of the framework to access and manipulate the domain
- objects by wrapping them in an adapter. This is an important point: the
- framework and its clients never interact with the domain objects
- directly.</para>
-
- <para>Note however that the <emphasis>metamodel</emphasis> module does
- not itself define the programming model conventions; that is the
- responsibility of the configured programming model (the default being
- the one defined in the <emphasis>default progmodel</emphasis>
- <package>(oai.progmodels:dflt)</package>.</para>
-
- <sect1>
- <title>Package Layering / Dependencies</title>
-
- <para>The packages that reside within <emphasis>core
- metamodel</emphasis> have break into the following layers (top layer
- packages depending on lower layers):</para>
+ <itemizedlist>
+ <listitem>
+ <para><classname>ApplicationScopedComponent</classname>, for
+ components that exist for the duration of the application</para>
+ </listitem>
- <screenshot>
- <screeninfo>Top-level Architecture Diagram with SpecLoader
- expanded</screeninfo>
+ <listitem>
+ <para><classname>SessionScopedComponent</classname> , for
+ components that are created a-new for each session, and</para>
+ </listitem>
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/metamodel/architecture-diagram-top-level-with-specloader-expanded.png"
- scale="100" />
- </imageobject>
- </mediaobject>
- </screenshot>
+ <listitem>
+ <para><classname>TransactionScopedComponent</classname>, for
+ components that are bound to a single transaction.</para>
+ </listitem>
+ </itemizedlist>
- <para>Note that the diagram shows the <package>specloader</package>
- package and also its subpackages. It also indicates that there is a
- tangle (bidirectional dependencies)<footnote>
- <para>Not a good thing, we recognize. But refactor to eliminate
- this would considerably complicate the codebase.</para>
- </footnote>Alternatively we can see the actual dependencies between
- packages (again, with that tangle highlighted):</para>
+ <para>For webapp/server-based deployments, a session is created for
+ each interaction<footnote>
+ <para>Just like JPA or Hibernate sessions.</para>
+ </footnote>. There is typically just one transaction per
+ session.</para>
+
+ <para>For client/standalone deployments, the session lasts for the
+ duration of the application, and so is one-to-one with the application
+ scope. For these cases a transaction is used to wrap each
+ client/server interaction<footnote>
+ <para>Strictly speaking, this is a statement about how the the
+ <emphasis>default runtime</emphasis> implementation works. Other
+ runtimes could conceivably take a different approach. If you are
+ only intending to use webapp-based viewers, then don't worry about
+ it... <emphasis>Isis</emphasis> works similarly to
+ JPA/Hibernate.</para>
+ </footnote>.</para>
- <screenshot>
- <screeninfo>Composition Diagram</screeninfo>
+ <para>Closely related to <classname>Component</classname> is the
+ <classname>Installer</classname> interface, which acts as a
+ <classname>Component</classname> factory. Each Installer provides a
+ type (a string) and a name (also a string), and the combination of
+ (type, name) is expected to be unique. For example, the DnD viewer has
+ a type of "viewer" and a name of "dnd".</para>
+
+ <para>This (type, name) combination is used to determine the
+ configuration files that are searched for when the
+ <classname>Component</classname> is created. Each
+ <classname>Component</classname>'s <classname>Installer</classname>
+ will search for at least two property files:
+ <filename>type.properties</filename> and
+ <filename>type_name.properties</filename>. For example, the DnD viewer
+ will search for both <filename>viewer.properties</filename> and also
+ <filename>viewer_dnd.properties</filename> file<footnote>
+ <para>In fact, it is possible for an
+ <classname>Installer</classname> to nominate additional property
+ files; this is sometimes appropriate where a
+ <classname>Component</classname> does double-duty and plays more
+ than one role. At the time of writing this capability was only
+ used by the components that install client/server remoting for the
+ <emphasis>default runtime</emphasis> module.</para>
+ </footnote>. An Installer can also indicate whether a missing config
+ file should be treated as an error or can be ignored (generally the
+ latter).</para>
+ </sect2>
+
+ <sect2 id="sec.ConfigurationApi">
+ <title><classname>IsisConfiguration</classname> and
+ <classname>IsisConfigurationBuilder</classname>
+ <acronym>API</acronym></title>
- <mediaobject>
- <imageobject>
- <imagedata fileref="images/metamodel/composition-diagram-top-level.png"
- scale="60" />
- </imageobject>
- </mediaobject>
- </screenshot>
- </sect1>
+ <para>The <classname>IsisConfigurationBuilder</classname> (in
+ <package>oai.core.commons.config</package> package) is used to hold
+ the "current" configuration; as (the <classname>Installer</classname>
+ for) <classname>Component</classname>s are loaded each indicates the
+ property file(s) to load, and these are used to update the current
+ configuration held within
+ <classname>IsisConfigurationBuilder</classname>. The "current"
+ configuration is initially just the properties in the
+ <filename>isis.properties</filename> file (see <xref
+ linkend="sec.ResourceStreamApi" /> for details on where this file is
+ actually loaded from) .</para>
+
+ <para>When the <classname>Component</classname> is actually
+ instantiated, it is handed an immutable
+ <classname>IsisConfiguration</classname> that can be thought of as a
+ snapshot of the set of properties held by the
+ <classname>IsisConfigurationBuilder</classname>. A consequence of this
+ design is that different <classname>Component</classname>s will have
+ references to different <classname>IsisConfiguration</classname>
+ objects; though all should always have access to "their"
+ properties.</para>
+
+ <para>Using properties specified in the configuration files is done by
+ get the <classname>IsisConfiguration</classname> singleton from the
+ context and using one of the lookup methods to get a value, as the
+ example below shows. The <literal
+ moreinfo="none">Configuration.ROOT</literal> constant provides the
+ base property name ("isis."). If no value is found with the specified
+ property name exists then null (or 0 or false) will be
+ returned.</para>
+
+ <programlisting format="linespecific">String formatRequired = getConfiguration().getString(Configuration.ROOT + "value.format.date");</programlisting>
+ </sect2>
+
+ <sect2>
+ <title
+ id="sec.ResourceStreamApi"><classname>ResourceStreamSource</classname>
+ <acronym>API</acronym></title>
- <sect1 id="sec.ObjectSpecifications">
- <title><classname>ObjectSpecification</classname>s and the
- <classname>SpecificationLoader</classname></title>
-
- <para>To make the domain objects useful within the framework the
- objects' public interfaces must be exposed. <emphasis>Isis</emphasis>
- uses a number of techniques to do this, but the predominant one is the
- Java reflection <acronym>API</acronym>s (in the
- <package>java.lang.reflect</package> package), a process we call
- introspection. These are used to determine what properties and
- collections an object has, what behaviour it can offer, and to find
- other information such as the object's title, a suggested order of its
- fields, and when its actions can or can't be used. It also is used to
- flag the type of object (abstract, lookup, object, value, and whether
- persistable); to refer to its superclass, any inteferfaces it
- implements and to list any subclasses.</para>
-
- <para>The details about this interface are recorded in an instance of
- <classname>ObjectSpecification</classname> (in the
- <package>oai.core.metamodel.spec</package> package). As each class of
- domain object is loaded into the system its corresponding instance of
- <classname>ObjectSpecification</classname> is generated. You can think
- of <classname>ObjectSpecification</classname> as analogous to
- <classname>java.lang.Class</classname>.</para>
-
- <sect2>
- <title><classname>SpecificationLoader</classname> component</title>
-
- <para>The specification object can be retrieved directly, by name or
- class, from the <classname>SpecificationLoader</classname>
- component<footnote>
- <para>For historical reasons the
- <classname>SpecificationLoader</classname> component is also
- sometimes called the reflector; indeed
- <classname>ObjectReflector</classname> is a subinterface that is
- used internally.</para>
- </footnote></para>
-
- <para>. When a domain object is used within the framework the
- <classname>SpecificationLoader</classname> instance is asked for the
- <classname>ObjectSpecification</classname> of the domain object's
- class. The first time that a class is requested the loader is
- responsible for performing the introspection and creating a complete
- <classname>ObjectSpecification</classname>. Thereafter the
- specification is returned from a cache.</para>
-
- <para>The set of <classname>ObjectSpecification</classname>s built
- up by the <classname>SpecificationLoader</classname> are all those
- that are reachable from the service classes (defined in
- <filename>isis.properties</filename> configuration file under
- <emphasis>isis.services</emphasis> key). Because cycles between
- <classname>ObjectSpecification</classname>s are permitted (that is,
- <classname>ClassA</classname> can reference
- <classname>ClassB</classname> and <classname>ClassB</classname> can
- reference <classname>ClassA</classname>), the creation of
- <classname>ObjectSpecification</classname>s is actually a two-stage
- process. When a class' <classname>ObjectSpecification</classname> is
- being created, any prerequisite specifications (for its class
- members) will be created if necessary, however those prerequisites
- will not flagged as not yet "introspected". Only when those
- prerequisite <classname>ObjectSpecification</classname>s are
- actually requested by name will their introspection be formed. This
- prevents infinite loops from occurring in the
- <classname>SpecificationLoader</classname>.</para>
-
- <para>It is also possible - and common - to obtain the
- <classname>ObjectSpecification</classname> from the domain object's
- adapter (the <classname>ObjectAdapter</classname> interface,
- discussed in <xref linkend="sec.ObjectAdapter" />).</para>
-
- <sect3>
- <title>Accessing the
- <classname>SpecificationLoader</classname></title>
-
- <para>If using the <emphasis>default runtime</emphasis> module
- <package>(oai.runtimes:dflt)</package>, then the
- <classname>SpecificationLoader</classname> can be accessed using
- <package>oai.runtimes.dflt.runtime.context.IsisContext#getSpecificationLoader()</package>.
- It is an application-scoped component, meaning that a single
- instance is used for the duration of the application
- running.</para>
-
- <para>Other runtime implementations will (are likely to) use
- dependency injection to make the
- <classname>SpecificationLoader</classname> available.</para>
- </sect3>
- </sect2>
-
- <sect2 id="sec.ObjectMembers">
- <title><classname>ObjectMember</classname>s</title>
-
- <para>One of the main purposes of
- <classname>ObjectSpecification</classname> is to describe the
- structure of the domain object to which it relates, in other words
- the members of that object's class. These are represented by
- (sub-)interfaces of the <classname>ObjectMember</classname>
- interface (in the <package>oai.core.metamodel.spec.feature</package>
- package). The main sub-interfaces of
- <classname>ObjectMember</classname> are
- <classname>OneToOneAssociation</classname>,
- <classname>OneToManyAssociation</classname> and
- <classname>ObjectAction</classname>.</para>
-
- <para>The
- <methodname>ObjectSpecification#getProperties()</methodname> method
- returns a list of <classname>OneToOneAssociation</classname>s that
- represent the properties (eg
- <methodname>Order#getShipDate()</methodname> or
- <methodname>Order#getCustomer()</methodname>), while
- <methodname>ObjectSpecification#getCollections()</methodname>
- returns <classname>OneToManyAssociation</classname>s to represent
- collections (eg <methodname>Order#getLineItems()</methodname>).
- Properties and collections are typically rendered in some sort of
- form within a viewer. Any remaining public methods (eg
- <methodname>Order#cancel()</methodname>) are represented as actions,
- accessible using
- <methodname>ObjectSpecification#getActions()</methodname> and
- returning a list of <classname>ObjectAction</classname>s. These are
- typically rendered as menu items or links in viewers.</para>
-
- <para>The <classname>OneToOneAssociation</classname>,
- <classname>OneToManyAssociation</classname> and
- <classname>ObjectAction</classname> interfaces all provide the
- ability to interact with the underlying domain object, allowing
- viewers to determine whether the property/collection/action is
- visible, is enabled/disabled, and whether a new value/argument is
- valid.</para>
-
- <para>It is also possible to obtain individual class members. For
- example, an individual property can be accessed via the
- <methodname>getProperty(String)</methodname> method, where the sole
- parameter is the the identifier of the property. In the case of a
- property, its identifier will be the name of the property method
- with the <emphasis>get</emphasis> prefix removed, and the first
- character of the remaining string converted to lowercase, so
- <methodname>getCustomerId()</methodname> become
- <emphasis>customerId</emphasis>. A similar pattern is used for
- collections, while actions have an identifier that also takes into
- account the parameters. In fact, the rules for constructing
- identifiers are available within the applib, in the
- <classname>oai.applib.Identifier</classname> class.</para>
-
- <para>The complete list of properties/collections/actions is used
- for things like persistence and remoting, however user interfaces
- need to consider what properties they show to avoid making hidden or
- unauthorised properties visible. To selectively get hold of all
- associations (properties and collections) the
- <methodname>getAssociations(Filter<ObjectAssociation>)</methodname>
- method should be used<footnote>
- <para>Rather than reinvent a filter API, the core framework
- reuses the applib's
- <classname>oai.applib.filter.Filter<T></classname>
- class</para>
- </footnote>, allowing us to set up a search filter based on any
- criteria that might be relevant. Typically views are created using
- only dynamically visible properties (so hidden fields are not
- visible and do not have any screen space reserved form them).
- However, in the case of a table the view will require a column for
- each <emphasis>potentially</emphasis> available (statically visible)
- property has a column created for it, but only show a value in the
- cell if the property is visible for the object in question
- (dynamically visible). To support this, two useful predefined
- instances are the available:
- <methodname>ObjectAssociationFilters.STATICALLY_VISIBLE_ASSOCIATIONS</methodname>
- and the
- <methodname>ObjectAssociationFilters.dynamicallyVisible(ObjectAdapter)</methodname>
- factory method.</para>
- </sect2>
- </sect1>
-
- <sect1 id="sec.HowTheMetaModelIsBuiltUpInternally">
- <title>How the metamodel is built up internally</title>
-
- <para>As already explained, the
- <classname>SpecificationLoader</classname> is responsible for building
- up completed <classname>ObjectSpecification</classname>s, one for each
- class that is reachable within the domain model. Moreover to avoid
- cyclic dependencies, these <classname>ObjectSpecification</classname>s
- are built-up in two stages; initially they are created but not fully
- built (introspected); only when required does introspection take
- place.</para>
-
- <para>This section goes into some of the internals as to how
- introspection process works.</para>
-
- <sect2>
- <title><classname>FacetFactory</classname>s and
- <classname>Facet</classname>s</title>
-
- <para>The first thing that <emphasis>Isis</emphasis> does is to
- create a <classname>FacetedMethodsBuilder</classname> for each
- <classname>ObjectSpecification</classname>. This is a helper object
- that co-ordinates the identification of the object members (ie the
- properties, collections and actions) of the
- <classname>ObjectSpecification</classname>. Each such object member
- is represented as a <classname>FacetedMethod</classname>. You can
- think of this as analogous to
- <methodname>java.lang.reflect.Method</methodname>, and it does
- indeed wrap an instance of such a <classname>Method</classname>.
- We'll get onto the "faceted" part of that name in just a
- minute.</para>
-
- <para>The actual hard work of building up the metamodel, though, is
- done by a collection of <classname>FacetFactory</classname>s. Each
- <classname>FacetFactory</classname> is responsible for understanding
- a specific element of the programming model. For example, one
- <classname>FacetFactory</classname> looks for the
- <methodname>disableXxx()</methodname> method that is used to disable
- (grey out) an object member, another looks for
- <classname>@Hidden</classname> that will hide an object member,
- another looks for <classname>@RegEx</classname> that can be used to
- validate property proposed values or action parameter arguments.
- Each of these pieces of knowledge is represented as a
- <classname>Facet</classname>, and is attached to the corresponding
- <classname>FacetedMethod</classname> (hence its name).</para>
-
- <para>Each of the methods in <classname>FacetFactory</classname>
- retrieves a "context" object, which provides a mechanism to remove
- methods so that subsequent <classname>FacetFactory</classname>s do
- not consider them. For example, if a supporting
- <methodname>disableXxx()</methodname> method is noticed while
- processing a <methodname>getXxx()</methodname> property or
- collection, then the <classname>FacetFactory</classname> in question
- will remove this method. This design means that the
- <classname>FacetFactory</classname> that identifies actions - which
- are taken to be all "remaining" public actions - will not
- inadvertantly create an action for these supporting methods.</para>
-
- <para>If you explore the type hierarchy then you'll see that
- <classname>FacetMethod</classname> implements
- <classname>FacetHolder</classname>, and it is this interface through
- which the <classname>FacetFactory</classname>s work. You might also
- note that <classname>ObjectSpecification</classname> also implements
- <classname>FacetHolder</classname>, as does
- <classname>FacetedMethodParameter</classname>, which represents an
- action parameter. What that means is that
- <classname>FacetFactory</classname>s can also add
- <classname>Facet</classname>s to these other types too. The
- interface for <classname>FacetFactory</classname> reflects this,
- having methods to handle the processing of a class, a method and an
- action parameter. All of these can have Facets.</para>
-
- <para>To summarize: the Isis metamodel has a type that is equivalent
- to a <classname>java.lang.Class</classname>
- (<classname>ObjectSpecification</classname>), to a
- <classname>java.lang.reflect.Method</classname>
- (<classname>FacetedMethod</classname>) and to an action parameter
- (<classname>FacetedMethodParameter</classname>). Each of these can
- have a set of <classname>Facet</classname>s attached to it, and
- these <classname>Facet</classname>s <emphasis>are</emphasis> the
- metadata for each such element.</para>
- </sect2>
-
- <sect2>
- <title>Identifying object members</title>
-
- <para>Recall that the <classname>FacetedMethodsBuilder</classname>
- is responsible for co-ordinating the building of the
- <classname>FacetMethod</classname>s of its owning
- <classname>ObjectSpecification</classname>. The first step involves
- identifying the actual properties, collections and actions of that
- <classname>ObjectSpecification</classname> (based on the underlying
- <classname>java.lang.Class</classname>). To do this, the
- <classname>FacetedMethodsBuilder</classname> searches the set of
- <classname>FacetFactory</classname>s for a factory that implements
- the
- <classname>PropertyOrCollectionIdentifyingFacetFactory</classname>.
- From these two collections of <classname>FacetedMethod</classname>s
- are created, one set to represent the properties and the other to
- represent the collections.</para>
-
- <para>Once all properties and collections have been identified, all
- remaining <code>public</code> methods are assumed to be actions.
- These form a third set of
- <classname>FacetedMethod</classname>s.</para>
- </sect2>
-
- <sect2 id="sec.MemberLayoutArranger">
- <title>Ordering Members
- (<classname>MemberLayoutArranger</classname>)</title>
-
- <para>The <classname>FacetedMethodsBuilder</classname>'s job is done
- once all the <classname>FacetedMethod</classname>s have been
- identified. At this point, the
- <classname>ObjectSpecification</classname> takes over and completes
- the job of building itself. The first task is to re-order the
- identified members (still in the form of
- <classname>FacetMethod</classname>s). It does this by delegating to
- a <classname>MemberLayoutArranger</classname> which is used returns
- the members in the required order.</para>
-
- <para><note>
- <para>The current implementation of MemberLayoutArranger orders
- the members as per the <classname>@MemberOrder</classname>
- annotation. In the future this component may take responsibility
- for more sophisticated layout arranger, to handle column-based
- layouts. This will require a change to its
- <acronym>API</acronym>.</para>
- </note></para>
- </sect2>
-
- <sect2>
- <title>Creating <classname>ObjectMember</classname>s (wrapping
- <classname>FacetedMethod</classname>s)</title>
-
- <para>After the <classname>FacetedMethod</classname>s have been
- ordered, they are then wrapped in the appropriate subclass of
- <classname>ObjectMember</classname> (already discussed, see <xref
- linkend="sec.ObjectMembers" />), in other words as a
- <classname>OneToOneAssociation</classname>,
- <classname>OneToManyAssociation</classname> or as an
- <classname>ObjectAction</classname>. These objects provide a number
- of methods that allow the clients of the metamodel (eg specifically,
- viewers) to interact with underlying domain objects (see <xref
- linkend="sec.O" />) through the metamodel.</para>
- </sect2>
-
- <sect2 id="sec.FacetDecorator">
- <title>Decorating <classname>Facet</classname>s
- (<classname>FacetDecorator</classname>)</title>
-
- <para>The last major step of building the metamodel is to decorate
- any <classname>Facet</classname>s, using any registered
- <classname>FacetDecorator</classname>s. Decorated
- <classname>Facet</classname>s allow additional behaviour to be added
- to already identified <classname>Facet</classname>s, and so are
- useful for adding internationalization and (in the client/server
- remoting support provided by the <emphasis>default
- runtime</emphasis>), transactional control.</para>
-
- <para><classname>FacetDecorator</classname>s are specified as a
- comma-separated list in the <emphasis>isis.properties</emphasis>
- configuration file using the
- <code>isis.reflector.facet-decorators</code> key. For
- example:</para>
+ <para>The <classname>ResourceStreamSource</classname> interface (in
+ <package>oai.core.commons.resource</package> package) is an
+ abstraction over locating resource files. It is used predominantly to
+ locate configuration files (see <xref
+ linkend="sec.ConfigurationApi" />), with implementations to load from
+ the config directory or from the classpath.</para>
+
+ <para>Different implementations of
+ <classname>IsisConfigurationBuilder</classname> use
+ <classname>ResourceStreamSource</classname> in order to search for
+ config files in specific locations. In principle it would be
+ straightforward to write a new implementation of
+ <classname>ResourceStreamSource</classname> that loads config files
+ from some other location (eg LDAP, the Windows registry or a database)
+ and then write a new <classname>IsisConfigurationBuilder</classname>
+ to use it.</para>
+ </sect2>
+
+ <sect2>
+ <title>Encoding <acronym>API</acronym></title>
+
+ <para>The <package>oai.core.commons.encoding</package> package
+ provides a number of classes to support the custom serialization of
+ elements of any element:</para>
- <programlisting format="linespecific">isis.reflector.facet-decorators=resource-i18n</programlisting>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="images/common/encoding-classdiagram.png"
+ scale="30" />
+ </imageobject>
+ </mediaobject>
- <para>will install a <classname>FacetDecorator</classname> for
- internationalization that loads from a
- <classname>java.util.ResourceBundle</classname>.</para>
-
- <para>The core <classname>FacetDecorator</classname> implementations
- are in the <emphasis>core progmodel</emphasis> module (see <xref
- linkend="chp.ProgModel" />).</para>
-
- <para><note>
- <para>The <classname>FacetDecorator</classname> design actually
- predates use of <classname>Facet</classname>s within the Isis
- metamodel (we renamed it to
- <classname>FacetDecorator</classname> after the fact). If you
- dig into the <classname>Facet</classname> API you'll see that it
- supports the concept of an underlying
- <classname>Facet</classname>
- (<methodname>Facet#getUnderlyingFacet()</methodname>). At some
- stage we hope to remove <classname>FacetDecorator</classname>s
- completely and simply use the underlying
- <classname>Facet</classname> approach.</para>
- </note></para>
- </sect2>
- </sect1>
-
- <sect1 id="sec.MetaModelValidator">
- <title>MetaModel Validation
- (<classname>MetaModelValidator</classname>)</title>
-
- <para>After all <classname>ObjectSpecification</classname>s have been
- identified and loaded, the <classname>SpecificationLoader</classname>
- calls out to the configured <classname>MetaModelValidator</classname>
- (defined in the
- <package>org.apache.isis.core.metamodel.specloader.validator</package>
- package). This provides the ability to validate that all loaded types
- are valid. Precisely what "valid" means depends on the context; the
- default <classname>MetaModelValidator</classname> is a no-op. However,
- some plug-in modules for <emphasis>Isis</emphasis> might provide their
- own rules. For example, the <acronym>JPA</acronym> object
- store<footnote>
- <para>Note that at the time of writing the JPA object store was
- part of Isis, having originally been written as a sister project
- for the Naked Objects framework.</para>
- </footnote> requires that all domain objects that are annotated with
- <classname>javax.jpa.Entity</classname> provide an "id" property
- annotated with <classname>javax.jpa.Identifier</classname>. Or, you
- might wish to configure your own
- <classname>MetaModelValidator</classname> in order to enforce your own
- project-specific rules.</para>
-
- <para>The <classname>MetaModelValidator</classname> can be specified
- using the <code>isis.reflector.validator</code> key. For
- example:</para>
-
- <programlisting format="linespecific">isis.reflector.validator=com.mycompany.myproj.isis.MyMetaModelValidator</programlisting>
-
- <para>will install
- <classname>com.mycompany.myproj.isis.MyMetaModelValidator</classname>
- as the <classname>MetaModelValidator</classname>.</para>
- </sect1>
+ <para>The <classname>DataInputExtended</classname> and
+ <classname>DataOutputExtended</classname> interfaces are
+ straightforward extensions of <classname>java.io.DataInput</classname>
+ and <classname>java.io.DataOutput</classname> respectively, simply
+ adding the capability to serialize arrays of primitives. The
+ <classname>DataInputStreamExtended</classname> and
+ <classname>DataOutputStreamExtended</classname> implement these
+ interfaces, providing the ability to read from/write to an underlying
+ <classname>java.io.InputStream</classname>.</para>
+
+ <para>Finally, the <classname>Encodable</classname> interface defines
+ a contract for objects to write themselves to a
+ <classname>DataOutputStreamExtended</classname>, with an implied
+ contract that they can be re-constructed from a corresponding
+ <classname>DataInputStreamExtended</classname>.</para>
+
+ <para>The primary usage of the encoding API is to enable client/server
+ remoting, as supported by the <emphasis>default runtime</emphasis>
+ implementation. However, it is also used in order to create
+ <classname>Memento</classname>s of domain objects (again, a capability
+ of the <emphasis>default runtime</emphasis>). This is used by some
+ viewers in order to maintain a handle on transient (not-yet-persisted)
+ objects.</para>
+ </sect2>
+
+ <sect2>
+ <title><classname>AuthenticationSession</classname> Definition</title>
+
+ <para>The <classname>AuthenticationSession</classname> interface (in
+ the <package>oai.core.commons.authentication</package> package)
+ provides a representation of an authenticated user within the
+ system.</para>
+
+ <para>Also worth mentioning is the utility class
+ <classname>AuthenticationSessionUtils</classname> can be used to
+ create an <classname>oai.applib.security.UserMemento</classname>,
+ which is the corresponding type within the applib (that is, the
+ identity of the authenticated user as the domain objects understand
+ it).</para>
+
+ <para>The interface to actually authenticate users and create
+ <classname>AuthenticationSession</classname>s - namely
+ <classname>AuthenticationManager</classname> - is defined in the
+ <emphasis>core runtime</emphasis> module (see <xref
+ linkend="chp.Runtime" />). This shouldn't be confused with
+ <classname>AuthenticationSessionProvider</classname> which merely
+ returns the current <classname>AuthenticationSession</classname>
+ <emphasis>if one exists</emphasis>.</para>
+ </sect2>
- <sect1 id="sec.ProgrammingModelAPI">
- <title><classname>ProgrammingModel</classname>
+ <sect2>
+ <title><classname>Debuggable</classname>
<acronym>API</acronym></title>
- <para>As will be apparent from <xref
- linkend="sec.HowTheMetaModelIsBuiltUpInternally" />, the set of
- conventions that make up the programming model is determined by the
- set of <classname>FacetFactory</classname>s that are used to process
- each class as it is loaded by the
- <classname>SpecificationLoader</classname>.</para>
+ <para>The <classname>Debuggable</classname> interface(in the
+ <classname>oai.core.commons.debug</classname> package) is used by some
+ <classname>Component</classname>s in order to build structured string
+ representations of themselves for debug purposes. A good example is
+ the debug menu options available within the DnD viewer.</para>
+ </sect2>
+
+ <sect2>
+ <title>Hamcrest <classname>Matcher</classname>s</title>
+
+ <para>The <classname>IsisMatchers</classname> class (in the
+ <classname>oai.core.commons.matchers</classname> package) provides a
+ collection of <ulink url="http://hamcrest.org">Hamcrest
+ </ulink><classname>Matcher</classname>s for use in both tests and also
+ production code.</para>
+ </sect2>
+
+ <sect2>
+ <title><classname>Ensure</classname> API</title>
+
+ <para>The <classname>Ensure</classname> class (in the
+ <package>oai.core.commons.ensure</package> package) allows assertions
+ to be made about arguments, state or general context, and uses
+ Hamcrest <classname>Matcher</classname>s to express those
+ assertions.</para>
+ </sect2>
+ </sect1>
+ </chapter>
- <para>The <classname>ProgrammingModel</classname> (in the
- <package>org.apache.isis.core.metamodel.progmodel</package> package)
- class is used to define this set of
- <classname>FacetFactory</classname>s, and is looked up right at the
- beginning of the bootstrap process when the SpecificationLoader is
- being specified. The default <classname>ProgrammingModel</classname>
- is defined in the <emphasis>default progmodel</emphasis>
- [oai.progmodels:dflt] module, and corresponds to the set of
- conventions described in the applib documentation.</para>
-
- <para>You may have occasion when you want to modify the
- <classname>ProgrammingModel</classname>. For example, suppose you
- wanted to support a new annotation, for example
- <classname>@StringLengthBetween(3, 10)</classname> annotation intended
- to be applied to string properties and parameters. This would require
- a corresponding
- <classname>StringLengthBetweenFacetFactory</classname>. This
- <classname>FacetFactory</classname> would then need to be added to the
- <classname>ProgrammingModel</classname>.</para>
-
- <para>There are two ways in which you can register this new
- FacetFactory. The first is to create your own
- <classname>ProgrammingModel</classname> (typically by subclassing the
- default <classname>ProgrammingModel</classname>) and then call its
- <methodname>#addFactory(Class<? extends
- FacetFactory>)</methodname> method. Your new implementation should
- then be registered in the isis.properties configuration file using the
- <code>isis.reflector.facets</code> key:</para>
-
- <programlisting format="linespecific">isis.reflector.facets=com.mycompany.myproj.isis.MyProgrammingModel</programlisting>
-
- <para>will install
- <classname>com.mycompany.myproj.isis.MyProgrammingModel</classname> as
- the <classname>MetaModelValidator</classname>.</para>
-
- <para>Alternatively, if you are just tweaking the default
- ProgrammingModel, then you can simply use
- <code>isis.reflector.facets.include</code> and
- <code>isis.reflector.facets.exclude</code> keys to include/exclude
- facets. The value for each of these keys is a comma-separated
- list:</para>
+ <chapter id="chp.MetaModel">
+ <title><emphasis>Metamodel</emphasis> Module</title>
- <programlisting format="linespecific">isis.reflector.facets.include=com.mycompany.myproj.isis.StringLengthBetweenFacetFactory,\
- com.mycompany.myproj.isis.PositiveValuesOnlyFacetFactory</programlisting>
+ <abstract>
+ <para>Classes and interfaces in the
+ <package>oai.core.metamodel</package> module.</para>
+ </abstract>
- <para><note>
- <para>Isis' support for Groovy works by defining a custom
- <classname>ProgrammingModel</classname>.</para>
- </note></para>
- </sect1>
+ <para>The core <emphasis>metamodel</emphasis> module defines the
+ interfaces and classes that make up the <emphasis>Apache Isis</emphasis>
+ metamodel. This metamodel is at the very heart of
+ <emphasis>Isis</emphasis>, and used in numerous ways:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>by viewers to obtain information about the domain objects, so
+ that they can be rendered in a generic object-oriented user
+ interface;</para>
+ </listitem>
+
+ <listitem>
+ <para>by persistence mechanisms (within the <emphasis>default
+ runtime</emphasis> module, <package>(oai.runtimes:dflt)</package>) to
+ determine which data is to be persisted;</para>
+ </listitem>
+
+ <listitem>
+ <para>by client/server remoting (within the <emphasis>default
+ runtime</emphasis> module) , to marshall domain objects automatically
+ between different tiers;</para>
+ </listitem>
+
+ <listitem>
+ <para>to provide the ability to provide XML Snapshots (through the
+ <classname>XmlSnapshot</classname> utility class, in the
+ <emphasis>core runtime</emphasis> module,
+ <package>(oai.core:runtime)</package>).</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>In addition, the metamodel provides a mechanism for the framework
+ and the clients of the framework to access and manipulate the domain
+ objects by wrapping them in an adapter. This is an important point: the
+ framework and its clients never interact with the domain objects
+ directly.</para>
+
+ <para>Note however that the <emphasis>metamodel</emphasis> module does not
+ itself define the programming model conventions; that is the
+ responsibility of the configured programming model (the default being the
+ one defined in the <emphasis>default progmodel</emphasis>
+ <package>(oai.progmodels:dflt)</package>.</para>
- <sect1 id="sec.ObjectAdapter">
- <title><classname>ObjectAdapter</classname>s</title>
+ <sect1>
+ <title>Package Layering / Dependencies</title>
- <para><emphasis>Isis</emphasis> wraps each domain object in an
- <classname>ObjectAdapter</classname> (in the
- <package>oai.core.metamodel.adapter</package> package). The rest of
- the framework does not normally work with the domain objects directly,
- but via these adapters. This is typically done by asking the
- <classname>ObjectAdapter</classname> for its corresponding
- <classname>ObjectSpecification</classname> by way of its
- <methodname>#getSpecification()</methodname> method. This allows the
- viewers to query the state of the object. For example the statement
- <code>adapter.getSpecification().getProperties().get(0).get(adapter)</code>
- would retrieve the first value of the first property of the domain
- object held by the <emphasis>Isis</emphasis> referenced by
- <code>adapter</code>.</para>
+ <para>The packages that reside within <emphasis>core
+ metamodel</emphasis> break into the following layers (top layer packages
+ depending on lower layers):</para>
+
+ <screenshot>
+ <screeninfo>Top-level Architecture Diagram with SpecLoader
+ expanded</screeninfo>
- <para>The adapter also exposes facilities to allow the runtime to
- manage the lifecycle of the wrapped domain object.</para>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="images/metamodel/architecture-diagram-top-level-with-specloader-expanded.png"
+ scale="100" />
+ </imageobject>
+ </mediaobject>
+ </screenshot>
- <itemizedlist>
- <listitem>
- <para>the <methodname>#getOid()</methodname> method is used to
- return a unique object identifier (an instance of the
- <classname>Oid</classname> class in the
- <package>org.apache.isis.core.metamodel.adapter.oid</package>
- package)</para>
+ <para>Note that the diagram shows the <package>specloader</package>
+ package and also its subpackages. It also indicates that there is a
+ tangle (bidirectional dependencies)<footnote>
+ <para>Not a good thing, we recognize. But refactor to eliminate this
+ would considerably complicate the codebase.</para>
+ </footnote>Alternatively we can see the actual dependencies between
+ packages (again, with that tangle highlighted):</para>
- <para>This is an abstraction over a primary key, because it is
- guaranteed to also be unique for non-persisted objects (if the
- runtime supports non-persisted objects; the <emphasis>default
- runtime</emphasis> does)</para>
- </listitem>
+ <screenshot>
+ <screeninfo>Composition Diagram</screeninfo>
- <listitem>
- <para>the <methodname>#getVersion()</methodname> returns version
- information about the domain object through a
- <classname>Version</classname> object</para>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="images/metamodel/composition-diagram-top-level.png"
+ scale="60" />
+ </imageobject>
+ </mediaobject>
+ </screenshot>
+ </sect1>
- <para>This allowing runtimes to implement optimistic
- locking</para>
- </listitem>
+ <sect1 id="sec.ObjectSpecifications">
+ <title><classname>ObjectSpecification</classname>s and the
+ <classname>SpecificationLoader</classname></title>
+
+ <para>To make the domain objects useful within the framework the
+ objects' public interfaces must be exposed. <emphasis>Isis</emphasis>
+ uses a number of techniques to do this, but the predominant one is the
+ Java reflection <acronym>API</acronym>s (in the
+ <package>java.lang.reflect</package> package), a process we call
+ introspection. These are used to determine what properties and
+ collections an object has, what behaviour it can offer, and to find
+ other information such as the object's title, a suggested order of its
+ fields, and when its actions can or can't be used. It also is used to
+ flag the type of object (abstract, lookup, object, value, and whether
+ persistable); to refer to its superclass, any inteferfaces it implements
+ and to list any subclasses.</para>
+
+ <para>The details about this interface are recorded in an instance of
+ <classname>ObjectSpecification</classname> (in the
+ <package>oai.core.metamodel.spec</package> package). As each class of
+ domain object is loaded into the system its corresponding instance of
+ <classname>ObjectSpecification</classname> is generated. You can think
+ of <classname>ObjectSpecification</classname> as analogous to
+ <classname>java.lang.Class</classname>.</para>
+
+ <sect2>
+ <title><classname>SpecificationLoader</classname> component</title>
+
+ <para>The specification object can be retrieved directly, by name or
+ class, from the <classname>SpecificationLoader</classname>
+ component<footnote>
+ <para>For historical reasons the
+ <classname>SpecificationLoader</classname> component is also
+ sometimes called the reflector; indeed
+ <classname>ObjectReflector</classname> is a subinterface that is
+ used internally.</para>
+ </footnote></para>
+
+ <para>. When a domain object is used within the framework the
+ <classname>SpecificationLoader</classname> instance is asked for the
+ <classname>ObjectSpecification</classname> of the domain object's
+ class. The first time that a class is requested the loader is
+ responsible for performing the introspection and creating a complete
+ <classname>ObjectSpecification</classname>. Thereafter the
+ specification is returned from a cache.</para>
+
+ <para>The set of <classname>ObjectSpecification</classname>s built up
+ by the <classname>SpecificationLoader</classname> are all those that
+ are reachable from the service classes (defined in
+ <filename>isis.properties</filename> configuration file under
+ <emphasis>isis.services</emphasis> key). Because cycles between
+ <classname>ObjectSpecification</classname>s are permitted (that is,
+ <classname>ClassA</classname> can reference
+ <classname>ClassB</classname> and <classname>ClassB</classname> can
+ reference <classname>ClassA</classname>), the creation of
+ <classname>ObjectSpecification</classname>s is actually a two-stage
+ process. When a class' <classname>ObjectSpecification</classname> is
+ being created, any prerequisite specifications (for its class members)
+ will be created if necessary, however those prerequisites will not
+ flagged as not yet "introspected". Only when those prerequisite
+ <classname>ObjectSpecification</classname>s are actually requested by
+ name will their introspection be formed. This prevents infinite loops
+ from occurring in the
+ <classname>SpecificationLoader</classname>.</para>
- <listitem>
- <para>the #<methodname>getResolveState()</methodname> state
- returning lazy loaded state, via the
- <classname>ResolveSate</classname> object</para>
+ <para>It is also possible - and common - to obtain the
+ <classname>ObjectSpecification</classname> from the domain object's
+ adapter (the <classname>ObjectAdapter</classname> interface, discussed
+ in <xref linkend="sec.ObjectAdapter" />).</para>
+
+ <sect3>
+ <title>Accessing the
+ <classname>SpecificationLoader</classname></title>
+
+ <para>If using the <emphasis>default runtime</emphasis> module
+ <package>(oai.runtimes:dflt)</package>, then the
+ <classname>SpecificationLoader</classname> can be accessed using
+ <package>oai.runtimes.dflt.runtime.context.IsisContext#getSpecificationLoader()</package>.
+ It is an application-scoped component, meaning that a single
+ instance is used for the duration of the application running.</para>
+
+ <para>Other runtime implementations will (are likely to) use
+ dependency injection to make the
+ <classname>SpecificationLoader</classname> available.</para>
+ </sect3>
+ </sect2>
+
+ <sect2 id="sec.ObjectMembers">
+ <title><classname>ObjectMember</classname>s</title>
+
+ <para>One of the main purposes of
+ <classname>ObjectSpecification</classname> is to describe the
+ structure of the domain object to which it relates, in other words the
+ members of that object's class. These are represented by
+ (sub-)interfaces of the <classname>ObjectMember</classname> interface
+ (in the <package>oai.core.metamodel.spec.feature</package> package).
+ The main sub-interfaces of <classname>ObjectMember</classname> are
+ <classname>OneToOneAssociation</classname>,
+ <classname>OneToManyAssociation</classname> and
+ <classname>ObjectAction</classname>.</para>
- <para>This allows runtimes to know whether the datastore needs to
- be queried to bring back additional data as the user "walks the
- object graph".</para>
- </listitem>
- </itemizedlist>
+ <para>The <methodname>ObjectSpecification#getProperties()</methodname>
+ method returns a list of <classname>OneToOneAssociation</classname>s
+ that represent the properties (eg
+ <methodname>Order#getShipDate()</methodname> or
+ <methodname>Order#getCustomer()</methodname>), while
+ <methodname>ObjectSpecification#getCollections()</methodname> returns
+ <classname>OneToManyAssociation</classname>s to represent collections
+ (eg <methodname>Order#getLineItems()</methodname>). Properties and
+ collections are typically rendered in some sort of form within a
+ viewer. Any remaining public methods (eg
+ <methodname>Order#cancel()</methodname>) are represented as actions,
+ accessible using
+ <methodname>ObjectSpecification#getActions()</methodname> and
+ returning a list of <classname>ObjectAction</classname>s. These are
+ typically rendered as menu items or links in viewers.</para>
- <para>The <classname>Oid</classname> class in particular warrants
- further discussion.</para>
+ <para>The <classname>OneToOneAssociation</classname>,
+ <classname>OneToManyAssociation</classname> and
+ <classname>ObjectAction</classname> interfaces all provide the ability
+ to interact with the underlying domain object, allowing viewers to
+ determine whether the property/collection/action is visible, is
+ enabled/disabled, and whether a new value/argument is valid.</para>
+
+ <para>It is also possible to obtain individual class members. For
+ example, an individual property can be accessed via the
+ <methodname>getProperty(String)</methodname> method, where the sole
+ parameter is the the identifier of the property. In the case of a
+ property, its identifier will be the name of the property method with
+ the <emphasis>get</emphasis> prefix removed, and the first character
+ of the remaining string converted to lowercase, so
+ <methodname>getCustomerId()</methodname> become
+ <emphasis>customerId</emphasis>. A similar pattern is used for
+ collections, while actions have an identifier that also takes into
+ account the parameters. In fact, the rules for constructing
+ identifiers are available within the applib, in the
+ <classname>oai.applib.Identifier</classname> class.</para>
+
+ <para>The complete list of properties/collections/actions is used for
+ things like persistence and remoting, however user interfaces need to
+ consider what properties they show to avoid making hidden or
+ unauthorised properties visible. To selectively get hold of all
+ associations (properties and collections) the
+ <methodname>getAssociations(Filter<ObjectAssociation>)</methodname>
+ method should be used<footnote>
+ <para>Rather than reinvent a filter API, the core framework reuses
+ the applib's
+ <classname>oai.applib.filter.Filter<T></classname>
+ class</para>
+ </footnote>, allowing us to set up a search filter based on any
+ criteria that might be relevant. Typically views are created using
+ only dynamically visible properties (so hidden fields are not visible
+ and do not have any screen space reserved form them). However, in the
+ case of a table the view will require a column for each
+ <emphasis>potentially</emphasis> available (statically visible)
+ property has a column created for it, but only show a value in the
+ cell if the property is visible for the object in question
+ (dynamically visible). To support this, two useful predefined
+ instances are the available:
+ <methodname>ObjectAssociationFilters.STATICALLY_VISIBLE_ASSOCIATIONS</methodname>
+ and the
+ <methodname>ObjectAssociationFilters.dynamicallyVisible(ObjectAdapter)</methodname>
+ factory method.</para>
+ </sect2>
+ </sect1>
- <sect2>
- <title>Object Identifiers (<classname>Oid</classname>s)</title>
+ <sect1 id="sec.HowTheMetaModelIsBuiltUpInternally">
+ <title>How the metamodel is built up internally</title>
- <para>An <classname>Oid</classname> is an object identifier for
- every domain entity, and is typically assigned by the runtime. For
- persisted objects it is value is assigned by the object store, but
- for transient objects (if the configured runtime supports them) the
- framework will also assign an <classname>Oid</classname>, and will
- manage its mutation if the object changes its persistence state
- (from transient to persisted, or vice versa).</para>
-
- <para>This <classname>Oid</classname> is used to uniquely reference
- the same object either across space (client/server remoting calls
- between VMs) or across time (between a sequence of requests to a
- webapp, say). The <classname>Oid</classname> is unique and that
- means that the runtime can maintain a one-to-one mapping to the
- <classname>ObjectAdapter</classname>, and hence to the wrapped a
- domain object.<note>
- <para>Mapping the <classname>Oid</classname> to
- <classname>ObjectAdapter</classname> is an example of the
- identity map pattern. In the case of the <emphasis>default
- runtime </emphasis><package>[oai.runtimes:dflt]</package>, the
- mapping is actually both from <classname>Oid</classname> -->
- <classname>ObjectAdapter</classname>, and from domain object
- pojo --> <classname>ObjectAdapter</classname>. (The
- <classname>ObjectAdapter</classname> has references to its
- <classname>Oid</classname> and wrapped domain object pojo, so
- this makes both of these implicitly bidirectional
- mappings).</para>
- </note>Typically an <classname>Oid</classname> is also immutable,
- however its value may change if an object changes its persistence
- state. In this case the runtime is required to ensure that all
- mappings that it might hold (eg from <classname>Oid</classname> to
- <classname>ObjectAdapter</classname>) are correctly maintained. To
- support this the previous state of the <classname>Oid</classname> is
- copied so that <methodname>getPrevious()</methodname> now returns a
- copy of the original <classname>Oid</classname> (instead of null)
- and <methodname>hasPrevious()</methodname> will return
- <code>true</code>.</para>
+ <para>As already explained, the
+ <classname>SpecificationLoader</classname> is responsible for building
+ up completed <classname>ObjectSpecification</classname>s, one for each
+ class that is reachable within the domain model. Moreover to avoid
+ cyclic dependencies, these <classname>ObjectSpecification</classname>s
+ are built-up in two stages; initially they are created but not fully
+ built (introspected); only when required does introspection take
+ place.</para>
+
+ <para>This section goes into some of the internals as to how
+ introspection process works.</para>
+
+ <sect2 id="sec.FacetFactory">
+ <title><classname>FacetFactory</classname>s and
+ <classname>Facet</classname>s</title>
+
+ <para>The first thing that <emphasis>Isis</emphasis> does is to create
+ a <classname>FacetedMethodsBuilder</classname> for each
+ <classname>ObjectSpecification</classname>. This is a helper object
+ that co-ordinates the identification of the object members (ie the
+ properties, collections and actions) of the
+ <classname>ObjectSpecification</classname>. Each such object member is
+ represented as a <classname>FacetedMethod</classname>. You can think
+ of this as analogous to
+ <methodname>java.lang.reflect.Method</methodname>, and it does indeed
+ wrap an instance of such a <classname>Method</classname>. We'll get
+ onto the "faceted" part of that name in just a minute.</para>
+
+ <para>The actual hard work of building up the metamodel, though, is
+ done by a collection of <classname>FacetFactory</classname>s. Each
+ <classname>FacetFactory</classname> is responsible for understanding a
+ specific element of the programming model. For example, one
+ <classname>FacetFactory</classname> looks for the
+ <methodname>disableXxx()</methodname> method that is used to disable
+ (grey out) an object member, another looks for
+ <classname>@Hidden</classname> that will hide an object member,
+ another looks for <classname>@RegEx</classname> that can be used to
+ validate property proposed values or action parameter arguments. Each
+ of these pieces of knowledge is represented as a
+ <classname>Facet</classname>, and is attached to the corresponding
+ <classname>FacetedMethod</classname> (hence its name).</para>
+
+ <para>Each of the methods in <classname>FacetFactory</classname>
+ retrieves a "context" object, which provides a mechanism to remove
+ methods so that subsequent <classname>FacetFactory</classname>s do not
+ consider them. For example, if a supporting
+ <methodname>disableXxx()</methodname> method is noticed while
+ processing a <methodname>getXxx()</methodname> property or collection,
+ then the <classname>FacetFactory</classname> in question will remove
+ this method. This design means that the
+ <classname>FacetFactory</classname> that identifies actions - which
+ are taken to be all "remaining" public actions - will not
+ inadvertantly create an action for these supporting methods.</para>
+
+ <para>If you explore the type hierarchy then you'll see that
+ <classname>FacetMethod</classname> implements
+ <classname>FacetHolder</classname>, and it is this interface through
+ which the <classname>FacetFactory</classname>s work. You might also
+ note that <classname>ObjectSpecification</classname> also implements
+ <classname>FacetHolder</classname>, as does
+ <classname>FacetedMethodParameter</classname>, which represents an
+ action parameter. What that means is that
+ <classname>FacetFactory</classname>s can also add
+ <classname>Facet</classname>s to these other types too. The interface
+ for <classname>FacetFactory</classname> reflects this, having methods
+ to handle the processing of a class, a method and an action parameter.
+ All of these can have Facets.</para>
+
+ <para>To summarize: the Isis metamodel has a type that is equivalent
+ to a <classname>java.lang.Class</classname>
+ (<classname>ObjectSpecification</classname>), to a
+ <classname>java.lang.reflect.Method</classname>
+ (<classname>FacetedMethod</classname>) and to an action parameter
+ (<classname>FacetedMethodParameter</classname>). Each of these can
+ have a set of <classname>Facet</classname>s attached to it, and these
+ <classname>Facet</classname>s <emphasis>are</emphasis> the metadata
+ for each such element.</para>
+ </sect2>
+
+ <sect2>
+ <title>Identifying object members</title>
+
+ <para>Recall that the <classname>FacetedMethodsBuilder</classname> is
+ responsible for co-ordinating the building of the
+ <classname>FacetMethod</classname>s of its owning
+ <classname>ObjectSpecification</classname>. The first step involves
+ identifying the actual properties, collections and actions of that
+ <classname>ObjectSpecification</classname> (based on the underlying
+ <classname>java.lang.Class</classname>). To do this, the
+ <classname>FacetedMethodsBuilder</classname> searches the set of
+ <classname>FacetFactory</classname>s for a factory that implements the
+ <classname>PropertyOrCollectionIdentifyingFacetFactory</classname>.
+ From these two collections of <classname>FacetedMethod</classname>s
+ are created, one set to represent the properties and the other to
+ represent the collections.</para>
+
+ <para>Once all properties and collections have been identified, all
+ remaining <code>public</code> methods are assumed to be actions. These
+ form a third set of <classname>FacetedMethod</classname>s.</para>
+ </sect2>
+
+ <sect2 id="sec.MemberLayoutArranger">
+ <title>Ordering Members
+ (<classname>MemberLayoutArranger</classname>)</title>
+
+ <para>The <classname>FacetedMethodsBuilder</classname>'s job is done
+ once all the <classname>FacetedMethod</classname>s have been
+ identified. At this point, the
+ <classname>ObjectSpecification</classname> takes over and completes
+ the job of building itself. The first task is to re-order the
+ identified members (still in the form of
+ <classname>FacetMethod</classname>s). It does this by delegating to a
+ <classname>MemberLayoutArranger</classname> which is used returns the
+ members in the required order.</para>
- <note>
- <para>This feature is used by the <emphasis>default
- runtime</emphasis>'s [oai.runtimes:dflt] client/server remoting
- module. When an <classname>Oid</classname> with a previous value
- is persisted, the client-side runtime uses the previous
- <classname>Oid</classname> to obtain the original tranisent object
- from its local cache. The object is then removed from the cache,
- its <classname>Oid</classname> is updated (via the
- <methodname>copyFrom(Oid)</methodname> method) and then it is
- returned to the cache. The results in the newly persisted object
- having the new persistent <classname>Oid</classname> and it being
- accessible as such from the cache. At this point the original
- version's transient state will no longer be recognised.</para>
- </note>
+ <para><note>
+ <para>The current implementation of MemberLayoutArranger orders
+ the members as per the <classname>@MemberOrder</classname>
+ annotation. In the future this component may take responsibility
+ for more sophisticated layout arranger, to handle column-based
+ layouts. This will require a change to its
+ <acronym>API</acronym>.</para>
+ </note></para>
+ </sect2>
- <sect3>
- <title>Entity (owned) Collections</title>
+ <sect2>
+ <title>Creating <classname>ObjectMember</classname>s (wrapping
[... 2258 lines stripped ...]