You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2014/02/12 00:31:19 UTC
[22/51] [partial] ISIS-694: mothballing the docbkx folders.
http://git-wip-us.apache.org/repos/asf/isis/blob/7a7836e3/mothballed/docbkx/component/viewer/wicket/src/docbkx/guide/isis-wicket-viewer.md
----------------------------------------------------------------------
diff --git a/mothballed/docbkx/component/viewer/wicket/src/docbkx/guide/isis-wicket-viewer.md b/mothballed/docbkx/component/viewer/wicket/src/docbkx/guide/isis-wicket-viewer.md
new file mode 100644
index 0000000..f9728be
--- /dev/null
+++ b/mothballed/docbkx/component/viewer/wicket/src/docbkx/guide/isis-wicket-viewer.md
@@ -0,0 +1,2976 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+Preface
+=======
+
+The *Apache Isis Wicket Viewer* provides a customizable web-based viewer
+for *Apache Isis* implemented using the [Apache
+Wicket](http://wicket.apache.org) framework. This user guide is written
+for developers looking to write *Apache Isis* applications and deploy
+them using the *Wicket Viewer*. It assumes familiarity with writing
+*Isis* applications, but that is all that is needed to get an
+application up-and-running with the generic OOUI.
+
+Familiarity with *Apache Wicket* is useful to understand how the wicket
+viewer fits together, but is essential unless you want to start
+customizing the UI. If you're interested in learning more about *Wicket*
+itself, then [Wicket In Action](http://www.manning.com/dashorst/)
+(Martijn Dashort & Eelco Hillenius) is definitely worth reading.
+
+Introduction
+============
+
+> This introductory chapter describes the objective of the *Wicket
+> Viewer*, and the target audience for this guide.
+
+Introducing the Apache Isis Wicket Viewer
+-----------------------------------------
+
+The *Wicket Viewer* provides a generic OOUI web-based viewer for *Apache
+Isis* domain models, using [Apache Wicket](http://wicket.apache.org) as
+the underlying web framework.
+
+What this means for you, the developer, is that you can build a web
+application solely by developing the domain model as pojos. The *Wicket
+Viewer* will then render your domain objects in a web UI using ready
+provided *Wicket* Components.
+
+This generic UI provides a lot of functionality out-of-the-box:
+
+- Each domain entity is rendered as a form, with the appropriate
+ individual component type for its values. This entity form also
+ allows editable references to other objects, too.
+
+- Moreover, any collections of a domain entity (eg Order /
+ OrderDetail) are also automatically rendered as a list, a table, or
+ in any other appropriate representation. Both the scalar and
+ collection references let the end-user easily "walk the graph"
+ around your domain model.
+
+- Finally, the vuewer also renders actions (commands/behaviors) for
+ your domain objects, and lets these actions to be invoked. This is
+ what makes *Apache Isis*-based applications that much more than
+ simple CRUD applications.
+
+You can see examples of the UI in ?.
+
+Over and above this generic interface, you are then free to customize
+this interface in various ways. Your customizations can be general (eg
+providing a way to render a collection of Locatable objects in a
+google-maps mashup) or targetted (eg a particular way to render a
+ShoppingCartWizard). Generally these customizations take the form of
+bespoke *Wicket* Components, though simple customizations can be
+performed just by modifying the CSS. Again you can see some examples of
+these customizations in ?.
+
+*Apache Isis* webapps that use the *Wicket Viewer* are bootstrapped in
+the same as vanilla *Wicket* applications, with the *Wicket Viewer*
+providing an implementation of *Wicket*'s `WebApplication` class that
+additionally bootstraps the *Apache Isis* runtime. Authentication is
+performed using *Apache Isis*' own security mechanism (which itself is
+pluggable).
+
+The Wicket Viewer compared to other Wicket RAD Frameworks
+---------------------------------------------------------
+
+### Differences in Capability
+
+There are several frameworks that aim either to provide a back-end to
+*Wicket* and/or that aim to eliminate some of the boilerplate; notable
+examples are [Wicketopia](http://wicketopia.sourceforge.net), [Wicket
+RAD](http://sites.google.com/site/wicketrad) and
+[DataBinder](http://databinder.net/wicket/show/overview). There are also
+precanned archetypes, such as jWeekEnd's
+[LegUp](http://jweekend.co.uk/dev/LegUp) archetypes and
+[IoLite](http://wicketstuff.org/confluence/display/STUFFWIKI/Wicket-Iolite),
+that do some of the wiring for back-end technologies like
+[Spring](http://springframework.org) and
+[Hibernate](http://hibernate.org). So, how does *Wicket Viewer* for
+*Apache Isis* compare to them?
+
+Well, compared to *Wicketopia* for example, *Wicket Viewer* shares the
+following ideas:
+
+- both frameworks support pluggable editors for Javabean properties
+
+- both frameworks allow the metamodel to be enhanced declaratively
+ using annotations (eg @Length); these are picked up by the editors
+
+- both frameworks support (or aspire to support) the metamodel being
+ built from various sources, including for example *Hibernate* or
+ JSR-303 annotations etc. In the case of the *Wicket Viewer* this is
+ done by using *Apache Isis*' own metamodel.
+
+But there are some differences too; through *Wicket Viewer*'s use of the
+*Apache Isis* it:
+
+- allows the metamodel to be enhanced imperatively as well as
+ declaratively; naming conventions are used to associate supporting
+ methods with the properties
+
+- supports user-defined value types (using *Apache Isis*' @Value
+ annotation); important for domain-driven applications
+
+- supports three level of business rule (both declaratively and
+ imperatively): is the entity class member visible, is it usable, is
+ the value proposed valid. For most other frameworks, only the last
+ of these (validation of proposed values) is typically supported
+
+- supports properties which are references to other entities, not just
+ simple value types. In particular, the *Wicket Viewer*'s property
+ editor for references allows instances to be found by invoking
+ repositories in-situ
+
+- renders entity collections as well as entity properties
+
+- renders entity actions (commands), allowing more than simple CRUD
+ behavior to be exposed. Indeed, it's possible for the domain entity
+ to be immutable except through the invocation of actions
+
+The flip side of the *Wicket Viewer*'s more extensive metamodel support
+is that it uses *Apache Isis*' own metamodel API. In contrast,
+*Wicketopia* (only supporting properties and not collections or actions)
+can get by using java.bean.PropertyDescriptor from the JDK.
+
+Compared to frameworks and archetypes that use *Hibernate* for the
+backend, the *Wicket Viewer* (again by leveraging the rest of the
+*Apache Isis* framework) is more general here too. So, it has a Session
+concept that is analogous to a *Hibernate* session (and if using the
+*JPA ObjectStore* as a backend, the *Apache Isis*' Session just wraps
+the JPA provider's PersistenceContext *e*'s). But it also supports other
+back-end persistence stores too. Indeed, *Apache Isis*' default to
+support rapid development we usually use an in-memory object store.
+
+The *Wicket Viewer* can also handle non-persisted objects as well as
+persisted objects; with every managed object is stored in an identity
+map. This support for non-persisted objects is important because it is
+central to supporting specialized use cases, including managing
+workflows for entering data, and dashboards for analyzing data.
+
+A few other points worthy of mention:
+
+- The *Wicket Viewer* will automatically serialize both persisted and
+ non-persisted objects, irrespective of whether the pojo itself is
+ serializable. This is important if deploying on a cluster.
+
+- Through the *Apache Isis* core framework, every managed object also
+ automatically has any domain service dependencies automatically
+ injected into it),
+
+- Again, courtesy of *Apache Isis* core, every persisted object has a
+ unique, immutable and serializable object Id (OID). This can be a
+ very useful resource when integrating with external services such as
+ REST and ESBs.
+
+- *Apache Isis* core also provides automatic dirty tracking, and
+ optimistic locking.
+
+Downsides? Currently *Apache Isis* does not support some of the
+Enterprise Java APIs such as JTA, so interactions with domain services
+that ultimately delegate to transaction stores (such as JMS) cannot be
+enlisted in the same transaction as the persistence store. And,
+obviously, it introduces a dependency on the *Apache Isis* framework
+over and above technologies such as the JPA provider implementation.
+
+### Differences in Philosophy
+
+Aside from differences in capability, there's also a difference in
+philosophy.
+
+Frameworks such as *Wicketopia* make it easy to render a form to edit
+the properties of an entity, but the application developer remains in
+control of the rest of the application service layer, and in wiring the
+various pages together.
+
+With the *Wicket Viewer*, though, it is the framework that is in control
+of this layer too, because the links between pages are ultimately
+rendered by the property editor Components provided by the viewer, and -
+in rendering the links representing references - controls . What that
+also means is that every page rendered by the *Wicket Viewer* is always
+of either an object (typical case), or an action parameter dialog, or of
+a collection (the result of invoking an action).
+
+Within this constraint, there is a lot of flexibility, though, because
+every element of the page can be customized. Internally the *Wicket
+Viewer* uses the chain of responsibility pattern to determine how which
+Component type to use to render a page element. This works at a
+fine-grained level (eg a date editor) and also at a larger-scale, (eg
+rendering a collection of Locatable entities on a google maps). See ?
+for more on this.
+
+### Part of the *Apache Isis* framework
+
+The *Wicket Viewer* is only one of a number of viewers available for
+*Apache Isis*. What that means is that you can take your same domain
+objects model and deploy them in other architectures. For example, the
+DnD viewer is a rich, desktop GUI that employs a drag-n-drop metaphor
+(hence its name). It can be run as a standalone single-user application
+or in client/server mode. Even if you don't deploy this viewer in
+production, the way that it displays domain objects makes for a very
+good development tool: something akin to a UML design tool, only
+animated.
+
+*Apache Isis* also supports a number of different ways to test your
+application. Domain objects written to the *Apache Isis* programming
+model are just pojos, and so can easily be unit tested using frameworks
+such as [JUnit](http://junit.org) and [JMock](http://jmock.org). As a
+step up from that, *Apache Isis* provides its own integrations with
+JUnit, providing a "headless" programming model that wraps your domain
+objects in proxies. This allows unit testing as if through the lens of a
+GUI; trying to invoke an action that is disabled will throw an
+exception.
+
+Moving up to story testing/BDD, *Apache Isis* integrates with
+[Concordion](http://concordion.org). The business analyst specifies the
+behavior of the system in HTML, and then the developer wire up this
+specification to the domain model using a set of fixtures that interact
+with the domain objects following the same rules as the viewers. This
+allows application-level testing without the hassle of using a GUI
+testing framework such as *Selenium*.
+
+One final point: *Apache Isis* has a strong separation between its
+programming model and the framework that understands that programming
+model. So, your domain objects depend only on the *Apache Isis* applib
+(which defines annotations such as @RegEx), not the framework itself.
+This allows you to take your domain objects and deploy them on other
+frameworks later, if need be. The *Wicket Viewer* maintains this strong
+separation, defining its own applib for annotations and features unique
+to the *Wicket Viewer*.
+
+The *Wicket Viewer* compared to other *Apache Isis* viewers
+-----------------------------------------------------------
+
+Because the *Wicket Viewer* renders your domain model within a webapp,
+it is to some extent similar to the HTML viewer that is provided
+out-of-the-box by *Apache Isis* itself. However, unlike the HTML viewer,
+the *Wicket Viewer* is highly customizable. The *Wicket Viewer* itself
+essentially consists of a set of [Wicket](http://wicket.apache.org)
+Components that are used to render domain objects, individual member
+elements of domain objects, and indeed collections of domain objects.
+The components provided are sufficient to render any *Apache Isis*
+domain model. However, any developer with experience of *Wicket* can
+easily write their own components and register them with the *Wicket
+Viewer*. In this way, custom renderings of domain objects can be
+achieved.
+
+Typical Flow for Developing a Wicket Objects Application
+--------------------------------------------------------
+
+There are, of course, lots of ways to skin any given cat, but here's how
+you might go about developing an *Apache Isis* application to be
+deployed using the *Wicket Viewer*:
+
+- use the *Apache Isis* application archetype to create an outline
+ domain model, running against the in-memory object store. The
+ structure of a *Apache Isis* application is reviewed in ?.
+
+- (optionally) use the DnD viewer to define some of the basic domain
+ services and entities. Or, you might want to skip this step and
+ develop solely using Wicket.
+
+- run your application under *Wicket*, either using a built-in Jetty
+ web server (see ?) or as a regular webapp (see ?).
+
+- customize the look-n-feel to use your preferred fonts and logos by
+ updating the CSS (see ?). You can also use CSS to fine-tune the
+ layout for selected objects or object members (see ?).
+
+- continue to develop your domain application, identifying properties,
+ collections and defining behavior through actions. Also, define
+ fixtures to represent pre-canned scenarios to explore with your
+ domain expert. Check out the core *Apache Isis* documentation for
+ more on developing domain objects.
+
+- fine tune the set of Components used to render your entities. The
+ *Wicket Viewer* provides built-in Components to render every element
+ of your domain objects, and in some cases provides more than one
+ Component. When the *Wicket Viewer* provides a number of alternate
+ views like this, you might prefer only one. Or, you might use an
+ existing Component and adapt it into your own Component. In either
+ case, you'll need to fine-tune the set of ComponentFactorys (see ?).
+
+- implement custom representations of some entities (or collections of
+ entities), where it makes sense. For example, you might want to
+ display objects that have a location in a google maps mashup, or
+ objects that have a date/time in a calendar. Ultimately these custom
+ representations are also just ComponentFactorys. A number of
+ Components (including one for google maps and one for charting) are
+ described in ?; use these directly or use them as inspiration for
+ your own.
+
+- support specialized use cases, if you have any. That is, rather than
+ require the end-user to interact directly with persisted entities,
+ introduce transient process objects to manage workflow and bulk
+ input, or write transient report objects to provide dashboards
+
+- as you continue to develop your application, you may need to
+ integrate with external services. For example, you might want to
+ send out an email, or invoke a web service exposed by some other
+ system in your enterprise. Define an interface for these domain
+ services, and register their implementation in the *Apache Isis*
+ configuration file. See the main *Apache Isis* documentation for
+ more details on this.
+
+- ultimately your application will be ready to deploy. Before you do,
+ though, remember that you'll need to sort out persistence and
+ security (see ?)
+
+Enough verbiage. The next chapter is a run through of a simple
+application, screenshot by screenshot, so you can quickly assess whether
+the *Wicket Viewer* fits your needs.
+
+Application Walkthrough
+=======================
+
+> This chapter is a collection of screenshots to give you an idea of
+> what the
+> Wicket Viewer
+> can do.
+
+Most of the screenshots in this chapter require no specific GUI code; in
+fact the first six sections require no specialized code other than the
+domain objects. The GUI that you see is generated by the *Wicket Viewer*
+at runtime, directly from the domain model. You can find all the code
+for the domain objects in ?.
+
+Logging on, and the Application Services Menu
+---------------------------------------------
+
+The *Wicket Viewer* integrates with *Apache Isis* authentication
+mechanism (which is itself pluggable); so we start off with a login
+screen:
+
+![](images/010-login.png)
+
+The initial home page shows a welcome message and more importantly a
+menu bar for each of the registered services defined in the domain
+application. These application services are the start points for the
+user, allowing them to find existing objects and to create new ones. For
+example the `Employees` menu item corresponds to the EmployeeRepository
+class.
+
+![](images/020-services-bar.png)
+
+From the menu bar we can get to the menu items for each service. So, for
+example, the `Employees` domain service provides two actions,
+`All Employees` and `Find
+ Employees`. These are generated automatically from corresponding
+methods - allEmployees() and findEmployees() - in the EmployeeRepository
+service.
+
+![](images/030-services-action.png)
+
+Viewing Entities and Collections of Entities
+--------------------------------------------
+
+Because the `All Employees` action takes no arguments, invoking it just
+returns its results. In this case the action returns a collection (of
+Employees), and so the viewer renders the collection as a table. If the
+action had returned a single object, then that would have been rendered
+instead.
+
+![](images/040-action-result.png)
+
+Clicking on one of the links takes us to a page rendering that object,
+in this case an Employee. There is a form for the entity's properties on
+the left, and summary details on the right. In this particular case the
+entity has no collections; we'll see one that does shortly.
+
+![](images/050-entity-form.png)
+
+In the summary section we an image, a title and the entity actions. All
+these are rendered directly from a metamodel built by inspecting the
+entity's class. Just as we can invoke actions on the services, we can
+also invoke actions on the entities; for example, to view this
+Employee's Claims with the `Claims For,,,` action.
+
+![](images/060-entity-actions.png)
+
+As before, this action returns a collection (of Claims) and so is
+rendered as a table.
+
+![](images/070-entity-action-results.png)
+
+Clicking on a link to a Claim again renders the entity. This is rendered
+in a similar manner to the Employee entity seen earlier. However, the
+Claim entity also has a collection (of ClaimItems), so these are also
+rendered.
+
+![](images/080-entity-form-and-collections.png)
+
+Editing Objects
+---------------
+
+In *Apache Isis* applications, we modify objects either by invoking
+actions on them or by editing them directly. Which properties are
+editable is determined by the entity itself, with the *Wicket Viewer*
+providing an appropriate editor for each property type. In the following
+screenshot we see the Claim entity being edited.
+
+![](images/090-entity-editing-scalars.png)
+
+As well as supporting the editing of properties with value types
+(boolean, String, Date etc), the viewer also allows properties
+representing references to other entities to be edited. For example,
+each Claim has an `approver` (of type Approver, and implemented by
+Employee). Thus, the viewer gives us the ability to find an Employee
+from the `Employees` repository:
+
+![](images/100-entity-editing-references.png)
+
+Leaving us with the `approver` reference set up:
+
+![](images/110-entity-editing-references-result.png)
+
+Invoking Actions
+----------------
+
+Being able to edit entities makes it easy to build CRUD-style
+(create/read/update/delete) applications. The *Wicket Viewer* also
+allows arbitrary actions to be performed on entities. In the screenshot
+below, we see the `Add Item` action (corresponding to addItem() method
+on the Claim class) being called:
+
+![](images/120-entity-action.png)
+
+Because this action takes parameters, the viewer renders a dialog form
+for the user to complete. Just as with the entity editor, we can specify
+parameters of any type, including references to other entities if we
+wish). In this particular case the parameter types are just numbers and
+strings:
+
+![](images/130-entity-action-parameters.png)
+
+When the action is complete, the entity is modified, by adding a new
+ClaimItem into the Claim's `items` collection.
+
+![](images/140-entity-modified.png)
+
+Admittedly, this `Add Item` action just a CRUD-style action. But the
+business logic in the action could be arbitrarily complex. The other
+action in Claim is `Submit`, which could perform lots of business
+processing:
+
+![](images/150-entity-action-more-complex.png)
+
+This action also takes an argument, being a reference to an Approver:
+
+![](images/160-entity-action-reference-parameters.png)
+
+When the action is performed, the object's state is updated, along with
+any other processing. For example, a message could have been sent via an
+enterprise service bus to the processing department. Note the title of
+the Claim entity has also been updated:
+
+![](images/170-entity-title-updated.png)
+
+Business Rules
+--------------
+
+In addition to actions, we can also capture business rules by means of
+validation. For example, a Claim, once submitted, cannot be submitted
+again. With the *Wicket Viewer*, this is shown by a tooltip:
+
+![](images/180-entity-validation.png)
+
+*Apache Isis* supports three different types of validation: whether the
+class member (property, collection or action) is visible; whether it is
+usable, and, whether the proposed value or arguments are valid. Or, more
+pithily, "can you see it, can you use it, can you do it". The viewer
+surfaces all of these different validation rules in the GUI.
+
+View Components
+---------------
+
+The pages rendered by the *Wicket Viewer* are built from multiple
+*Apache Wicket* Components. For example, the application service menu
+bar has its own Component:
+
+![](images/200-view-components-application-service.png)
+
+Likewise, there's a Component to render an entire entity:
+
+![](images/205-view-components-entity-simple.png)
+
+And there's a Component to display the properties of an entity:
+
+![](images/210-view-components-entity-properties.png)
+
+And in turn there's a Component for every entity property:
+
+![](images/220-view-components-entity-property.png)
+
+There are Components such as these for every class member, including
+collections, actions and, indeed, action parameters. In fact, you can
+think of the *Wicket Viewer* as basically a collection of pre-canned
+Components that know how to render the *Apache Isis* metamodel.
+
+Note, by the way, the CSS classes. Each HTML element can be targetted
+either by its type (a string scalar), or by its class member (the
+Claim's `description` property), or both. The viewer therefore makes it
+easy to contribute custom CSS that applies to every page.
+
+Alternate Views
+---------------
+
+As we've seen already, there's a Component to render an entire entity.
+In fact, it's possible to provide more than one Component to do this. We
+could also view the entity in a tabbed view:
+
+![](images/230-multiple-views-for-entities.png)
+
+When there is more than one Component capable of rendering the object,
+the viewer allows the view to be selected:
+
+![](images/230-multiple-views-for-entities.png)
+
+This works for collections too; using the selector we can view a
+collection of Employees in a variety of ways:
+
+![](images/240-multiple-views-for-collections.png)
+
+Selecting the `icons` view shows the collection of Employees as icons:
+
+![](images/270-customize-ui-icons.png)
+
+So far all the screenshots we've seen have been generated from a basic
+domain application, with no custom GUI coding (see ?). But the *Wicket
+Viewer* is also extensible. So, we can write custom views/components,
+provide a corresponding ComponentFactory and then plug them into the
+*Wicket Viewer*'s registry of ComponentFactorys. For example, if we make
+Employee implement a view-specific Locatable interface, then we can
+render them on a google-maps mashup:
+
+![](images/280-customize-ui-maps-mashup.png)
+
+The selector, by the way, is actually implemented as yet another
+view/component, capable of rendering the entity or collection. It
+queries the ComponentFactory registry to determine how many other
+ComponentFactorys there are capable of rendering the entity or
+collection; if more than one then it provides a drop-down and then
+delegates to the other Components to do the work.
+
+Specialized Use Cases
+---------------------
+
+Being able to easily render entities and collection of entities without
+any custom UI coding is great for being able to develop an understanding
+of the problem domain. However, it doesn't always make sense to let the
+user interact directly with the domain objects. For example, if the
+domain objects are very fine-grained such that clicking into them would
+be tedious for the user to do, it makes sense to introduce another
+object that collect the required data and walk the graph of domain
+objects on the users behalf. Or, more straight forwardly, the use case
+might be particularly complex or subtle, and we want to provide the user
+with additional guidance.
+
+The *Wicket Viewer* therefore lets us work with objects designed to
+guide the user through the use case. Because they represent a particular
+solution to help the user achieve their objective, you can think of them
+as being part of the solution space (whereas regular domain objects
+belong to the problem space). Another name also given for objects of
+this type is "process objects"; they take the user through a particular
+process.
+
+For example, we might have a wizard that takes the user through the
+process of making a new Claim:
+
+![](images/300-process-objects.png)
+
+The object this action returns is not a Claim, instead it is a
+ClaimWizard. Unlike Claim, this is not persisted; its state is bound to
+a particular users' session. The design of the ClaimWizard is like any
+other wizard, taking the user through a number of pages; first an
+introductory page:
+
+![](images/310-wizard-intro-page.png)
+
+After that we are taken through pages for each of the properties; For
+example the next page prompts for the Claim's `claimant`:
+
+![](images/320-wizard-claimant-page.png)
+
+The Claim's `approver` and `description` properties likewise have the
+own pages, for example:
+
+![](images/330-wizard-approver-page.png)
+
+The final page allows the user to review details, then confirm:
+
+![](images/340-wizard-finish-page.png)
+
+On finish, the ClaimWizard will create and persists the Claim.
+
+Process objects like wizards are primarily concerned with inputting
+data. We can also have objects that are tailored towards the output of
+data, eg for reporting. For example, we could have a (non-persisted)
+ClaimSummary object that sums up Claim amounts by Claimant:
+
+![](images/400-analyze-claim-summary.png)
+
+We can then combine this with custom views, eg to represent a collection
+of such ClaimExpenseSummarys as a pie chart:
+
+![](images/410-analyze-summary-piechart.png)
+
+There's some guidance on writing application code to deal with such
+specialized use cases in ?.
+
+Running the Application
+=======================
+
+> This chapter describes how to take an *Apache Isis* application and
+> get it running using the *Wicket Viewer*, with the non-customized,
+> generic OOUI.
+
+Applications deployed to run with the *Wicket Viewer* are, ultimately
+just *Wicket* webapps that happen to boot up *Apache Isis*. As such,
+they are bootstrapped with a `web.xml` that is structured the same as
+any other *Wicket* application.
+
+This chapter describes how to run up such a webapp starting with a
+regular *Apache Isis* application.
+
+Structure of an Apache Isis Application
+---------------------------------------
+
+We recommend that you use *Apache Isis*' Maven application archetype to
+set up your application. Doing so will result in a multi-module project
+that contains the following modules:
+
+- `app`
+
+ Main (parent) module, whose `pom.xml` references the submodules
+
+- `app/dom`
+
+ Domain object model, plus interfaces for services, repositories and
+ factories
+
+- app/domsvc
+
+ Implementation of domain services (not repositories/factories)
+
+- `app/objectstore-default`
+
+ Implementation of repositories/factories for the default object
+ store
+
+- `app/fixture`
+
+ Fixtures, used to seed (in-memory) object store when running in
+ exploration/prototype mode
+
+- `app/commandline`
+
+ Bootstrap for running from the command line (typically, the DnD
+ viewer or HTML viewer)
+
+- `app/viewer-wicket`
+
+ The application set up to be run using wicket, and packaged up as a
+ webapp.
+
+You will see that the archetypes creates a number of other
+`app/viewer-xxx` and `app/objectstore-xxx` modules. You can delete those
+modules that aren't relevant to your final deployment (probably the
+`app/viewer-xxx` modules and most of the `app/objectstore-xxx` modules,
+or you can just ignore them. If you do delete any modules, remember to
+remove their reference from the parent `app/pom.xml` file'a \<modules\>
+section).
+
+### The viewer-wicket module's web.xml
+
+The `viewer-wicket` module aready contains a `web.xml` file to define
+the web app, in the `src/main/webapp` directory. The contents of this is
+largely boilerplate, and will be very familiar if you already know the
+*Apache Wicket* framework:
+
+ <?xml version="1.0" encoding="ISO-8859-1"?>
+ <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+ version="2.4">
+
+ <display-name>claims</display-name>
+ <filter>
+ <filter-name>wicket.app</filter-name>
+ <filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
+ <init-param>
+ <param-name>applicationClassName</param-name>
+ <param-value>org.apache.isis.viewer.wicket.viewer.IsisWicketApplication</param-value>
+ </init-param>
+ </filter>
+
+ <filter-mapping>
+ <filter-name>wicket.app</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+
+ </web-app>
+
+The IsisWicketApplication is a subclass of *Wicket*'s own WebApplication
+that bootstraps *Apache Isis*, handles authentication and sets up
+sessions and transactions.
+
+### Deployment Mode
+
+*Apache Wicket* webapps can be run in one of two "configuration type"s,
+either in the `development` configuration type or in the `deployment`
+configuration type. In Wicket, the configuration type can be specified
+as either:
+
+1. A system property: `-D
+ wicket.configuration`
+
+2. as a servlet specific `<init-param>`
+
+3. as a context specific `<context-param>`
+
+Setting this value changes various properties, such as whether templates
+are reloaded. Wicket's Application\#configure() method is the place to
+look for all the differences.
+
+For its part, *Apache Isis* defines various "deployment mode"s. For
+example, `exploration` and `prototype` mode are both intended for
+single-user development, with the former requiring no login and also
+including any actions annotated as `@Exploration`. For multi-user
+(production) use, *Apache Isis* provides the `server` deployment mode,
+associating a separate *Apache Isis* runtime (IsisContext) to each
+thread (ie bound to a `ThreadLocal`).
+
+The *Wicket Viewer* maps *Wicket*'s `development` configuration type to
+*Apache Isis*' `prototype` deployment mode. However, the `server`
+deployment mode provided by *Apache Isis* is not quite appropriate for a
+*Wicket* webapp, because there could be multiple concurrent requests for
+a given user (originating from the same browser/user agent). The *Wicket
+Viewer* therefore defines a custom deployment mode which binds the
+*Apache Isis* runtime to the *Wicket* session (see the
+IsisContextForWicket class if you're interested in such things).
+
+What all this means is that selecting between *Apache Isis* deployment
+modes is done just by specifying the *Apache Wicket* configuration type.
+If you're already familiar with Wicket there's therefore nothing new to
+learn: just configure the webapp to run in either `development` or
+`deployment` mode.
+
+### Security
+
+Since both of the deployment modes supported by *Wicket Objects* require
+a login, it means we need to set up security. The IsisWicketApplication
+class provided by the *Wicket Viewer* subclasses from *Wicket*'s own
+AuthenticatedWebApplication, and serves up a sign-in page. To ensure
+that this sign-in page appears, every web page served up by the *Wicket
+Viewer* is annotated with
+`@AuthorizeInstantiation("org.apache.isis.viewer.wicket.roles.USER")`,
+which requires that every login has a role called
+`org.apache.isis.viewer.wicket.roles.USER`.
+
+*Apache Isis* deployment modes provide a couple of ways of avoiding
+login during development. For example, in `exploration` mode the viewers
+automatically login, while in `exploration` mode and `prototype` mode
+specifying a `LogonFixture` will means an automatic logon using the
+details provided within that fixture. The *Wicket Viewer* does not
+support `exploration` mode, however, and does not currently support
+LogonFixture. What that means is that it's always necessary to login.
+
+The *Wicket Viewer* delegates to *Apache Isis* to authentication.
+*Apache Isis*' default authentication mechanism is a no-op and requires
+no configuration. If you decide to a different authentication mechanism
+(eg file-based security, org.apache.isis.alternatives.security:file, or
+LDAP-based security, org.apache.isis.alternatives.security:ldap) then
+refer to the appropriate documentation for that module.
+
+The *Wicket Viewer* simply always places each login into the
+`org.apache.isis.viewer.wicket.roles.USER` role, to ensure that once the
+sign-in page is navigated past, that the user can access each web page.
+
+One consequence of this design is that the roles cannot currently be
+used by the *Apache Isis* domain objects nor the authorization
+mechanism; every user is simply in the USER role. This will be addressed
+in the future.
+
+Running as a WAR
+----------------
+
+The most straightforward approach for running a *Wicket
+Viewer*application is to build a WAR archive from the `viewer-wicket`
+module. Most IDEs will then allow this WAR to be deployed on an external
+servlet container, for debugging. For example, in Eclipse this is done
+by Run As \> Server:
+
+![](images/RunAsServer.png)
+
+and then select the external servlet container to deploy against:
+
+![](images/RunAsServer-dialog.png)
+
+If deploying this way, then the web browser URL may be slightly
+different. For example with Eclipse the URL to specify is
+<http://localhost:8080/webappname>.
+
+Running using *Isis*' WebServer class
+-------------------------------------
+
+As an alternative to deploying as a webapp, you can use Isis' WebServer
+class. This class has its own main() to bootstraps Jetty and configures
+a webapp from the `web.xml` file. If using Eclipse, for example, then yo
+can create a `.launch` configuration to run your application using
+WebServer.
+
+### Adding Dependency to Bootstrap
+
+The only thing you need to do is to edit the `viewer-wicket` module's
+`pom.xml`; add/comment in:
+
+ <dependency>
+ <groupId>org.apache.isis.core</groupId>
+ <artifactId>webserver</artifactId>
+ </dependency>
+
+### Running the Application
+
+Running the application is just a matter of running the
+org.apache.isis.WebServer. For example, in Eclipse the following launch
+configuration should suffice:
+
+![](images/WebServer-launch.png)
+
+Running this will boot strap a Jetty webserver:
+
+![](images/WebServer-running.png)
+
+You can then log on using <http://localhost:8080>:
+
+![](images/WicketObjects-logon-page.png)
+
+Customizing the Startup
+-----------------------
+
+Most of the remaining chapters address various means by which the UI
+generated by the *Wicket Viewer* can be customized. Before we go any
+further, though, we should mention that the viewer uses dependency
+injection provided by [Guice](http://google-guice.googlecode.com). It
+comes with default bindings, so customizing the *Wicket Viewer* requires
+overriding these default bindings.
+
+Note: this usage of Guice is likely to be replaced by JSR-299 based
+dependency mechanism.
+
+Specifically, in IsisWicketApplication, there's a method
+newIsisWicketModule():
+
+ public class IsisWicketApplication extends AuthenticatedWebApplication {
+ ...
+ protected Module newIsisWicketModule() {
+ return new IsisWicketModule();
+ }
+ }
+
+This method can be overridden in your own subclass of
+IsisWicketApplication, providing a new implementation of *Guice*'s
+Module interface. Typically you'll just want to override one or two of
+the bindings, so you should use the Modules helper class:
+
+ public class MyApplication extends IsisWicketApplication {
+ ...
+ protected Module newIsisWicketModule() {
+ return Modules.override(super.newIsisWicketModule()).with(
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ // override bindings here
+ }
+ }
+ );
+ }
+ }
+
+To pick up your subclass of IsisWicketApplication, just update the
+`web.xml` file.
+
+Customizing CSS
+===============
+
+> This chapter describes how to customize the UI generated by the
+> *Wicket Viewer* by providing application-specific CSS.
+
+CSS is used heavily by the *Wicket Viewer*, with the HTML of each
+Component using CSS classes for the styling. But you can override the
+default styling by supplying an application-level CSS file.
+
+By default this file should be called `application.css` and should
+reside within `WEB-INF` directory.
+
+There are two levels at which we can override the default styling,
+either generically or specific to a particular domain. Let's look at
+both.
+
+Generic CSS
+-----------
+
+The name of each class indicates the nature of what is being rendered,
+and each Component provides some default styling so that the
+out-of-the-box UI is usable. For example, an entity icon, title and
+actions are rendered as:
+
+![](images/EntitySummary.png)
+
+The icon and title for this are rendered as:
+
+ <div class="entitySummary">
+ <img src="?wicket:interface=:4:entity:entity:entitySummary:entityImage:1:IResourceListener::"
+ class="entityImage" wicket:id="entityImage">
+ <label class="entityTitle" wicket:id="entityTitle">Fred Smith</label>
+ ...
+ </div>
+
+So, to change the font, you could use:
+
+ div.entitySummary > label.entityTitle {
+ color: maroon;
+ font-size: xx-large;
+ font-weight: bolder;
+ }
+
+This would render the entity icon and title as:
+
+![](images/EntitySummary-Css.png)
+
+In this way you can develop a look-n-feel for the application (or
+perhaps your organization).
+
+Specific CSS
+------------
+
+As well as targetting HTML elements generically, individual class
+members can also be targetted for a particular domain model.
+
+For example, the properties of a Claim object might be rendered as:
+
+![](images/Claim-properties.png)
+
+The HTML for the description property is:
+
+ <div class="Claim-description" wicket:id="scalar">
+ <wicket:panel>
+ <div class="string scalarPanel">
+ <label wicket:id="scalarIfRegular" for="scalarValue3d">
+ <span class="scalarName" wicket:id="scalarName">description</span>
+ <span class="scalarValue">
+ <input type="text" title="" size="25" disabled="disabled"
+ value="Meeting at clients" wicket:id="scalarValue"
+ id="scalarValue3d"
+ name="properties:1:scalar:scalarIfRegular:scalarValue">
+ </span>
+ </label>
+ <span wicket:id="feedback">
+ <wicket:panel>
+ </wicket:panel>
+ </span>
+ </div>
+ </wicket:panel>
+ </div>
+
+To change the label of this specific element, we could use:
+
+ .Claim-description .scalarName {
+ color: maroon;
+ font-weight: bolder;
+ }
+
+which would give us:
+
+![](images/Claim-properties-CSS.png)
+
+This is a slightly trite example, but demonstrates the point.
+
+Changing the location/name of the application CSS file
+------------------------------------------------------
+
+As already mentioned, the default for the application CSS file is in
+`WEB-INF/application.css`. If for any reason you want to change this,
+you can do so by subclassing IsisWicketApplication and overriding the
+newIsisWicketModule() method.
+
+For example, to use `css/myapp.css` (under `WEB-INF`) you would write:
+
+ public class MyApplication extends IsisWicketApplication {
+ ...
+ protected Module newIsisWicketModule() {
+ return Modules.override(super.newIsisWicketModule()).with(
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bindConstant().annotatedWith(ApplicationCssUrl.class).to("css/myapp.css");
+ }
+ }
+ );
+ }
+ }
+
+Your custom application should then be registered in `web.xml`, as
+described in ?.
+
+Using the Wicket Viewer AppLib
+==============================
+
+> This chapter explains how to use certain viewer-specific features in
+> your domain application.
+
+Many *Isis* components have their own application library, the *Wicket
+Viewer* included. This allows you to specify additional viewer-specific
+semantics within your domain model; the viewer can then render them
+appropriately.
+
+Configuring the Project Modules
+-------------------------------
+
+In order to use the viewer-specific features, the `dom` module must
+reference the applib, and the `viewer-wicket` module must be configured
+appropriately.
+
+### Referencing the AppLib
+
+In the dom project, add in a `<dependency>` to the *Wicket Viewer*
+applib:
+
+ <dependencies>
+ ...
+
+ <!-- Wicket Viewer -->
+ <dependency>
+ <groupId>org.apache.isis.viewer.wicket</groupId>
+ <artifactId>applib</artifactId>
+ </dependency>
+
+ ...
+ </dependencies>
+
+This will allow you to use the *Wicket Viewer*'s annotations etc. in
+your domain objects.
+
+### Configuring the *Wicket Viewer* Facets
+
+In addition, you must also configure your application so that the *Isis*
+metamodel contains the information to be picked up by the *Wicket
+Viewer*.
+
+In the `viewer-wicket` module, add the following to `isis.properties`:
+
+ isis.reflector.facets.include=\
+ org.apache.isis.viewer.wicket.metamodel.wizardpagedesc.WizardPageDescriptionAnnotationFacetFactory\
+ ,org.apache.isis.viewer.wicket.metamodel.cssclass.CssClassAnnotationFacetFactory
+
+This basically instructs *Apache Isis* to capture additional information
+(facets) in its metamodel. Now let's look at what these facets are used
+for.
+
+@CssClass
+---------
+
+The @CssClass annotation allows you to specify an additional CSS class
+(or classes) applied on any object type, property, collection or action.
+This CSS will be added verbatim to any CSS classes that are normally
+applied by the *Wicket Viewer* itself.
+
+\*\*\* TODO: complete.
+
+There is further discussion on
+
+@WizardPageDescription
+----------------------
+
+It's common for wizards to have a description explaining what the user
+is expected to do. This can be modeled as a String property annotated
+with @WizardPageDescription, for example:
+
+ @NotPersistable
+ public class ClaimWizard extends AbstractDomainObject {
+
+ ...
+
+ @WizardPageDescription
+ @MemberOrder(sequence = "1")
+ public String getPageDescription() { ... }
+
+ ...
+ }
+
+Adding this annotation causes the *Wicket Viewer* to select a different
+Component to be used to render this property; specifically, one that
+renders the value as a large label. You can see the result of this
+annotation in below (the label above the claimant).
+
+![](images/320-wizard-claimant-page.png)
+
+Note that this annotation only affects the description; the three
+buttons shown in the above screenshot are as a result of the wizard
+being implemented as a custom component. For more on that topic, see ?.
+
+Customizing the Component Set
+=============================
+
+> This chapter describes the how to customize the UI by modifying the
+> set of components used to render objects, properties, collections or
+> actions.
+
+The *Wicket Viewer* offers several ways in which the UI rendered can be
+customized, from simply tweaking the CSS (see ?) through to the use of
+custom components for mashups and wizards. This chapter explains how to
+use pre-existing custom components; ? explains how to write your own
+components, and lists some components that have already been developed.
+
+There are more details provided in each of the sections that follow.
+
+Component Factories
+-------------------
+
+### ComponentFactory interface
+
+At its heart the *Wicket Viewer* consists of a set of *Wicket*
+`Component`s and corresponding IModel\<?\>s that are used to render
+entities, collections of elements and indeed individual members of
+elements. Each Components is created by a corresponding
+ComponentFactory, with the factory to use discovered using a
+chain-of-responsibility pattern.
+
+The Components created by the *Wicket Viewer* vary in size from
+rendering an entire collection of entities all the way down to a single
+property of an entity. You can find the full set of built-in Components
+by searching for implementations of ComponentFactory:
+
+![](images/ComponentFactory-hierarchy.png)
+
+For example, the CollectionContentsAsAjaxTableFactory class is used to
+render a collection of entities (eg returned from an action invocation)
+as, erm, an ajax table;
+
+ public class CollectionContentsAsAjaxTableFactory extends ComponentFactoryAbstract {
+ private static final long serialVersionUID = 1L;
+ private static final String NAME = "styled";
+
+ public CollectionContentsAsAjaxTableFactory() {
+ super(ComponentType.COLLECTION_OF_ENTITIES, NAME);
+ }
+
+ @Override
+ public boolean appliesTo(IModel<?> model) {
+ return model instanceof EntityCollectionModel;
+ }
+
+ public Component createComponent(String id, IModel<?> model) {
+ EntityCollectionModel collectionModel = (EntityCollectionModel) model;
+ return new CollectionContentsAsAjaxTable(id, collectionModel);
+ }
+ }
+
+The selection of the ComponentFactory is based on two criteria: the
+ComponentType, and the IModel\<?\>. Broadly speaking the ComponentType
+standardizes the `wicket:id` used in the HTML fragment (so
+`<div wicket:id="collectionContents"/>` would map onto the
+ComponentType.COLLECTION\_CONTENTS, while the IModel\<?\> is the
+corresponding information used for the rendering of that component. But
+there's a semi-formal relationship between these two concepts; the
+ComponentType effectively acting as a power-type for the subclass of
+IModel\<?\> that is supplied.
+
+The superclass ComponentFactoryAbstract takes responsibility for
+checking that the ComponentType matches the `wicket:id`, while
+delegating the checking of the IModel to its subtype:
+
+ public abstract class ComponentFactoryAbstract implements ComponentFactory ... {
+ ...
+ public final boolean appliesTo(ComponentType componentType, IModel<?> model) {
+ return componentType == getComponentType() && appliesTo(model);
+ }
+
+ protected abstract boolean appliesTo(IModel<?> model);
+ ...
+ }
+
+The subclass then refines this check by overriding appliesTo() to also
+check the model; returning true indicates that the ComponentFactory is
+able to render that model, after which the createComponent() method is
+then called to actually create the instance. So in
+CollectionContentsAsAjaxTableFactory, its implementation simply checks
+if the supplied model is an EntityCollectionModel. More
+sophisticated/less generic ComponentFactory might also make additional
+checks; you can find some examples of these in ?.
+
+### Registering ComponentFactorys using META-INF Services
+
+The easiest way to register new ComponentFactorys is using the JDK's own
+ServiceLoader capability.
+
+All that is needed is for your ComponentFactory to be registered in a
+file on the classpath call
+`META-INF/services/org.apache.isis.viewer.wicket.ui.ComponentFactory`.
+The contents of this file should be the fully qualified class name of
+your ComponentFactory implementation. And that's it! Registering the
+ComponentFactory is done automatically just by virtue of updating the
+classpath. You'll find that the custom components described in ? all use
+this technique.
+
+Note that this technique does not allow you to remove existing
+ComponentFactorys, only add new ones. So if you do want to exclude any
+of the built-in ComponentFactorys, then you will still need to use the
+ComponentFactoryList method.
+
+> **Note**
+>
+> This capability is provided by the ComponentFactoryRegistry component,
+> which is also bound in the IsisWicketModule. If you wanted to, you
+> could replace this higher-level registry component with your own
+> implementation. There's probably very little need to do this though,
+> given that you can just as easily customize using ComponentFactoryList
+> or `META-INF` services.
+
+### Registering ComponentFactorys using a custom ComponentFactoryList
+
+The set of ComponentFactorys that are provided with the *Wicket Viewer*
+are specified by the ComponentFactoryList interface.
+
+If you only want to add new factories, we recommend using the technique
+described in ?. But if you want to remove support for any of the default
+factories, or perhaps change the order in which factories are
+registered, you'll need to do use write and the bind in your own
+implementation of ComponentFactoryList.
+
+To write your implementation of ComponentFactoryList, start with the
+default implementation, ComponentFactoryListDefault. You'll see that it
+is written so that it can be easily subclassed and overridden on an
+as-needed basis. Worst case scenario, you can copy-and-paste code as
+necessary.
+
+Then, you need to override the binding in newIsisWicketModule(), eg:
+
+ public class MyApplication extends IsisWicketApplication {
+ ...
+ protected Module newIsisWicketModule() {
+ return Modules.override(super.newIsisWicketModule()).with(
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(ComponentFactoryList.class).to(MyComponentFactoryList.class);
+ }
+ }
+ );
+ }
+ }
+
+You should also ensure that your MyComponentFactoryList is annotated
+with @Singleton.
+
+And, do remember to update `web.xml` to reference your subclass of
+IsisWicketApplication.
+
+Advanced Customization
+----------------------
+
+This section covers a couple of more advanced customization topics.
+
+### Page Registry
+
+While the content of any given web page rendered by the *Wicket Viewer*
+is made up of multiple Components, there are in fact only a small number
+of WebPages:
+
+- WelcomePage displays the initial home page with a welcome message
+
+- EntityPage displays a single entity
+
+- ActionPage displays an action dialog or the results of invoking an
+ action.
+
+Each of these has a corresponding HTML page which defines the content of
+that page. In many cases the look-n-feel of these pages can be adjusted
+simply using CSS, as described in ?. If necessary though an entirely
+different page layout can be specified, for example to put the menubar
+on the left rather than at the top.
+
+The easiest approach to define a new page is to subclass PageAbstract
+superclass and then provide a different implementation of PageRegistry.
+As for ComponentFactorys, this is done by providing a new
+implementation, and then overriding a binding in newIsisWicketModule().
+
+The default pages are specified by PageClassListDefault:
+
+ public class PageClassListDefault implements PageClassList {
+
+ @Override
+ pulic void registerPages(PageRegistrySpi pageRegistry) {
+ pageRegistry.registerPage(PageType.SIGN_IN, WicketSignInPage.class);
+ pageRegistry.registerPage(PageType.SIGN_OUT, WicketSignOutPage.class);
+ pageRegistry.registerPage(PageType.ENTITY, EntityPage.class);
+ pageRegistry.registerPage(PageType.HOME, HomePage.class);
+ pageRegistry.registerPage(PageType.ACTION, ActionPage.class);
+ }
+ }
+
+You can easily copy-n-paste this to create your own implementation. Note
+though that there must be a registered page for every PageType instance,
+otherwise the *Wicket Viewer* will fail fast on boot time.
+
+Overriding the binding is done in the usual way:
+
+ public class MyApplication extends IsisWicketApplication {
+ ...
+ protected Module newIsisWicketModule() {
+ return Modules.override(super.newIsisWicketModule()).with(
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(PageClassList.class).to(MyPageClassList.class);
+ }
+ }
+ );
+ }
+ }
+
+Don't forget to update the web.xml to specify your subclass of
+IsisWicketApplication.
+
+> **Note**
+>
+> If all you want is to provide a custom rendering of a particular
+> interface or class, then you should instead write and register a
+> ComponentFactory, with a ComponentType.ENTITY and filtering the
+> EntityModel. The custom components described in ? do this, as does the
+> component registered in the test application to render a wizard (see
+> ?).
+
+### Subclassing IsisWicketApplication
+
+As we've seen, you can also customize *Wicket Objects* in various ways
+by subclassing the IsisWicketApplication bootstrap. The most common
+reason for doing so is to override the default implementation of
+ComponentFactoryList.
+
+This design follows the general style of *Wicket*; in fact, you'll see
+that IsisWicketApplication itself overrides a number of other methods
+(such as newRequestCycle() and newConverterLocator()), in order to hook
+*Apache Isis* into the rest of Wicket.
+
+In general it's unlikely that you'll need to alter the behavior of these
+hook methods; but it's useful to know that *Wicket Objects* doesn't
+particularly interfere with the way in which you may be used to
+customizing regular *Wicket* applications.
+
+Writing Custom Components
+=========================
+
+> This chapter provides some further guidance on objects designed to
+> support specialized use cases.
+
+Back in the application walkthrough (see ?) we saw that the *Wicket
+Viewer* has support for non-persisted objects that are designed to
+support specialized use cases. Whereas regular persisted domain objects
+can be thought of as part of the problem space, such non-persisted
+objects can be thought of as being part of the solution space because
+they offer a particular solution to a particular user objective. We call
+these objects *process objects* because objects they help a user perform
+a particular process.
+
+This chapter provides some general guidance on writing such process
+objects, and outlines the support that exists in *Wicket Objects* for
+writing custom components for such objects.
+
+Custom Components for Process Objects
+-------------------------------------
+
+Because process objects are there to guide the user, they often go
+hand-in-hand with custom components so that they can be rendered in a
+particular way.
+
+For example, in the application walkthrough we saw that a ClaimWizard
+process object is rendered with its previous(), next() and finish()
+actions as regular buttons:
+
+![](images/320-wizard-claimant-page.png)
+
+Similarly, the ClaimExpenseSummary object (or rather, a collection of
+them) hooks into the capabilities of the googlecharts component
+(discussed in ?):
+
+![](images/410-analyze-summary-piechart.png)
+
+You can use any of the *Wicket Viewer*'s built-in Components that are
+used to build the generic views for your own custom views; search down
+the ComponentFactory inheritance hierarchy and there's a good chance
+you'll find something of use. For example, you can easily add components
+to represent properties in a wizard (the custom Component for
+ClaimWizard does precisely this; you can inspect the code in ?).
+
+The *Wicket Viewer* also has a small number of Components intended for
+writing custom views.
+
+### ProcessObjectPanelAbstract
+
+The ProcessObjectPanelAbstract is intended to be used as the superclass
+for any panel-like Component that will render a process object. Its
+model is intended to be an EntityModel, which wraps the process object.
+
+This adapter class contains a number of convenience methods to help
+build panels:
+
+- addProperties(Form\<?\> form, String id)
+
+ This adds the currently visible properties from the process object
+ to the provided form
+
+- isValid(Form\<?\> form)
+
+ This validates the process object, where the form holds the
+ properties
+
+- executeNoArgAction(String actionId)
+
+ Executes the indicated action on the process object (expected to
+ take no-arguments).
+
+Although minimal, these methods are sufficient to build a basic wizard.
+The executeNoArgAction() method, for example, allows `Previous`, `Next`
+and `Finish` buttons to be added.
+
+### Help Wanted!
+
+As you can see, the level of support provided by the *Wicket Viewer* for
+custom views is, admittedly, quite limited. There's nothing to prevent
+you from writing your own, of course, but to do so you'll need to use
+with the *Apache Isis* metamodel APIs.
+
+We hope to extend the components available as *Wicket Objects* continues
+to be developed. But in the meantime, if you build a Component that you
+think would be generally useful, please consider contributing it back to
+this project for the benefit of others.
+
+Use a Page enum for Wizards
+---------------------------
+
+Here's just a bit of good old-fashioned advice; consider using an enum
+to track the state of your wizards.
+
+For example, the ClaimWizard in the example app has the following enum
+defined:
+
+ @NotPersistable
+ public class ClaimWizard extends AbstractDomainObject {
+
+ public enum Page {
+ INTRO("This wizard will take you through the process of creating a claim"),
+ CLAIMANT("Enter the claimant that is making this claim"),
+ APPROVER("By default, the claimant's own approver will approve this claim. " +
+ "Update here if another approver will approve this claim."),
+ DESCRIPTION("Update the description if required."),
+ SUMMARY("Confirm all details, or go back and amend if needed");
+
+ private String description;
+ private Page(String description) {
+ this.description = description;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public boolean hasPrevious() {
+ return ordinal() > 0;
+ }
+ public Page previous() {
+ if (hasPrevious()) {
+ return values()[ordinal() - 1];
+ } else {
+ return this;
+ }
+ }
+
+ public boolean hasNext() {
+ return ordinal() < values().length - 1;
+ }
+ public Page next() {
+ if (hasNext()) {
+ return values()[ordinal() + 1];
+ } else {
+ return this;
+ }
+ }
+
+ @Ignore
+ public boolean is(Page... pages) {
+ for (Page page : pages) {
+ if (page == this) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ ...
+ }
+
+This not only defines the descriptions of each Page, it also includes
+some logic for the previous() and next() actions to delegate to:
+
+ @NotPersistable
+ public class ClaimWizard extends AbstractDomainObject {
+
+ // {{ Page
+ private Page page;
+ @Hidden
+ public Page getPage() { ... }
+ public void setPage(final Page page) { ... }
+ // }}
+
+
+ // {{ Page Description
+ @WizardPageDescription
+ @MemberOrder(sequence = "1")
+ public String getPageDescription() { ... }
+
+ // {{ previous
+ @MemberOrder(sequence = "1")
+ public void previous() {
+ setPage(getPage().previous());
+ }
+ public String disablePrevious() {
+ return coalesce(noPreviousPage(), confirmIfOnSummaryPage());
+ }
+ private String noPreviousPage() {
+ return !getPage().hasPrevious() ? "no previous page" : null;
+ }
+ // }}
+
+ // {{ next
+ @MemberOrder(sequence = "2")
+ public void next() {
+ setPage(getPage().next());
+ }
+ public String disableNext() {
+ return coalesce(noNextPage(), confirmIfOnSummaryPage());
+ }
+ private String noNextPage() {
+ return !getPage().hasNext() ? "no next page" : null;
+ }
+ // }}
+
+ ...
+ }
+
+Custom Components in isis-contrib
+=================================
+
+> This chapter describes a number of custom components for the *Wicket
+> Viewer*. Some of these integrate third party components and/or
+> experimental.
+
+As was described in ?, the *Wicket Viewer* is designed to be extensible,
+allowing you to plug in more sophisticated renderings of the domain
+objects that make up your application. This chapter describes a number
+of custom components that demonstrate this capability, most of which are
+basically wrappers around functionality within the *Wicket*'s companion
+[WicketStuff](http://wicketstuff.org) project.
+
+The components here are probably best considered as examples rather than
+formally part of the *Wicket Viewer*, if only because we want the
+*Wicket Viewer* to depend just on core *Wicket*, not *WicketStuff*. But
+what you will find is that all the components here follow a similar
+layout, so you can easily adapt copy them into your own projects and
+adapt them as you feel fit.
+
+About the Components
+--------------------
+
+The source for these components is available at \*\*\*.
+
+### Common Layout
+
+Most of the components define their own interfaces or annotations; these
+are then implemented or annotated on the domain classes so that the
+component knows whether it applies or not (see discussion on
+ComponentFactory, in ?).
+
+To minimize the coupling between the domain objects and the component
+implementation, we separate out the interfaces/annotations into an
+applib.
+
+![](images/views-common-layout.png)
+
+The naming convention for these modules is:
+
+- `org.starobjects.wicket:view-xxx` for the parent module for view
+ 'xxx'
+
+- `org.starobjects.wicket:view-xxx-applib` for the applib submodule
+
+- `org.starobjects.wicket:view-xxx-view` for the view submodule (that
+ contains the actual ComponentFactory and Component implementations)
+
+### Adding Dependency Management for Custom Views
+
+In the parent project's `pom.xml`, specify the modules of the custom
+views that you want to use, along with the version:
+
+ <dependencyManagement>
+ <dependencies>
+ ...
+
+ <!-- Wicket Viewer view extensions -->
+ <dependency>
+ <groupId>org.apache.isis.viewer.wicket</groupId>
+ <artifactId>view-gmap2-applib</artifactId>
+ <version>${wicketobjects.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.isis.viewer.wicket</groupId>
+ <artifactId>view-gmap2-view</artifactId>
+ <version>${wicketobjects.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.isis.viewer.wicket</groupId>
+ <artifactId>view-googlecharts-applib</artifactId>
+ <version>${wicketobjects.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.isis.viewer.wicket</groupId>
+ <artifactId>view-googlecharts-view</artifactId>
+ <version>${wicketobjects.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.isis.viewer.wicket</groupId>
+ <artifactId>view-calendarviews-applib</artifactId>
+ <version>${wicketobjects.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.isis.viewer.wicket</groupId>
+ <artifactId>view-calendarviews-view</artifactId>
+ <version>${wicketobjects.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.isis.viewer.wicket</groupId>
+ <artifactId>view-cooldatasoftmenu-view</artifactId>
+ <version>${wicketobjects.version}</version>
+ </dependency>
+
+ ...
+ <dependencies>
+ </dependencyManagement>
+
+### Adding the Custom View's AppLibs as Dependencies
+
+\*\*\* Again, if you intend to use any of the custom components (see ?),
+then also add in dependencies to their respective applibs (if they have
+one):
+
+ <dependencies>
+ ...
+
+ <!-- Wicket Viewer view extensions -->
+ <dependency>
+ <groupId>org.starobjects.wicket</groupId>
+ <artifactId>view-calendarviews-applib</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.starobjects.wicket</groupId>
+ <artifactId>view-gmap2-applib</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.starobjects.wicket</groupId>
+ <artifactId>view-googlecharts-applib</artifactId>
+ </dependency>
+
+ ...
+ </dependencies>
+
+### Update Classpath
+
+The classpath for both the `dom` submodule and the `commandline` /
+`webapp` submodule each need to be updated (see ? for an overview of the
+typical structure of an *Apache Isis* application):
+
+- the `dom` submodule should be updated to reference the
+ view-xxx-applib submodule for each custom component
+
+- the `commandline` / `webapp` module should be updated to reference
+ the `view-xxx-view` submodule for each custom component
+
+Gmap2
+-----
+
+The gmap2 component renders a collection of objects in a Google map:
+
+![](images/280-customize-ui-maps-mashup.png)
+
+All that is required is for the object to implement Locatable interface,
+which in turn returns a Location value object:
+
+ package org.apache.isis.viewer.wicket.view.gmap2.applib;
+
+ public interface Locatable {
+ Location getLocation();
+ }
+
+If deploying on localhost, no API key is required. However, internet
+deployments do require an key, which should be specified as an
+init-parameter for the *Wicket* filter in `web.xml`:
+
+ <?xml version="1.0" encoding="ISO-8859-1"?>
+ <web-app ... >
+
+ ...
+
+ <filter>
+ <filter-name>wicket.app</filter-name>
+ <filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
+ <init-param>
+ <param-name>applicationClassName</param-name>
+ <param-value>org.apache.isis.viewer.wicket.viewer.app.IsisWicketApplication</param-value>
+ </init-param>
+ <init-param>
+ <param-name>GOOGLE_MAPS_API_KEY</param-name>
+ <param-value>(key here)</param-value>
+ </init-param>
+ </filter>
+
+ </web-app>
+
+Google Charts
+-------------
+
+The googlechart component provides basic charting capabilities. It
+currently supports a single chart type; to render a collection as a pie
+chart:
+
+![](images/410-analyze-summary-piechart.png)
+
+All that is required is for the object to implement the (horribly named)
+PieChartable:
+
+ package org.apache.isis.viewer.wicket.view.googlecharts.applib;
+
+ public interface PieChartable {
+
+ double getPieChartValue();
+ String getPieChartLabel();
+ }
+
+The label is used to point to each sector on the pie chart, the value
+determines the size of each sector relative to the other sectors.
+
+CoolDataSoft Application Services Menu
+--------------------------------------
+
+The CoolDataSoft application services menu provides a different
+look-n-feel for the application services menu, using Ajax instead of
+CSS:
+
+![](images/cooldatasoft-appmenu.png)
+
+The implementation is based upon code lifted from the
+[wicket-menu](http://code.google.com/p/wicket-menu/) project, hosted on
+code.google.com. Please note that this code is GPLv3 and so cannot be
+used freely in commercial applications.
+
+Deployment Topics
+=================
+
+> This chapter touches on various topics that should be addressed prior
+> to deployment.
+
+Before you can deploy your application into production there are a
+number of things to be addressed. Most significantly of these is
+persistence, but security is another important topic.
+
+Because the *Wicket Viewer* runs on top of *Apache Isis*, many of the
+deployment tasks are based on the way in which *Apache Isis* tackles
+them.
+
+This chapter briefly outlines the main tasks from a *Wicket Objects*
+perspective. You might, though, want to dig out my book, [Domain Driven
+Design using Naked Objects](http://pragprog.com/titles/dhnako), for more
+in-depth coverage of the *Apache Isis* side-of-things (it covers *Apache
+Isis*' predecessor, *Naked Objects*, but is still broadly applicable).
+
+Running in a WebApp
+-------------------
+
+When developing *Apache Isis* applications you can run from either the
+`commandline` project or from the `webapp` project (see ?). If you've
+been using the former, then you'll need to switch to running from the
+latter so that your application can be built as a WAR for deployment.
+Take care to ensure that:
+
+- the classpath dependencies are the same (so that any custom
+ components you're using or have written are picked up)
+
+- that the `isis.properties` config file is the same
+
+Persistence
+-----------
+
+If you've been using the in-memory object store for development,
+obviously you'll need to switch to a persistent object store before you
+deploy.
+
+Going into the details of that is outside the scope of this guide, but
+it's worth noting that you have a number of options:
+
+- the simplest persistence mechanism (albeit still only really for
+ prototyping) is to use the XML object store. You can specify this in
+ `isis.properties`:
+
+ isis.persistor=xml
+ isis.xmlos.dir=/tmp/xml
+
+- more likely though you'll want to use a relational database. One
+ option is [JPA Objects](http://jpaobjects.sourceforge.net), another
+ sister project to *Apache Isis* (like *Wicket Objects* itself, in
+ fact). There's reasonable coverage in the
+ [DDDuNO](http://pragprog.com/titles/dhnako) book.
+
+- *Apache Isis* also has (will have) a JDBC-based object store, SQL
+ Object Store
+
+- If relational databases aren't your thing, *Apache Isis* also has
+ (will have) a BerkeleyDB Object Store
+
+Security
+--------
+
+By default, *Wicket Objects* is configured to use *Apache Isis*' default
+authentication and authorization. This are both file-based, with a
+simple passwords file to define users, and a similar file to define
+authorization. *Apache Isis* does though provide an implementation for
+both that use LDAP. This is discussed in the other *Apache Isis*
+documentation and in Dan Haywood's book. Alternatively, you could always
+write your own implementations to hook into your own security
+infrastructure.
+
+See also ?.
+
+Wicket DEPLOYMENT mode
+----------------------
+
+Finally, you'll also want to switch into Wicket deployment mode (ie for
+production). This is done in the normal way, by modifying *web.xml*:
+
+ <?xml version="1.0" encoding="ISO-8859-1"?>
+ <web-app ... >
+
+ ...
+
+ <filter>
+ <filter-name>wicket.app</filter-name>
+ <filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
+ <init-param>
+ <param-name>applicationClassName</param-name>
+ <param-value>org.apache.isis.viewer.wicket.viewer.app.IsisWicketApplication</param-value>
+ </init-param>
+ <init-param>
+ <param-name>configuration</param-name>
+ <param-value>deployment</param-value>
+ </init-param>
+ </filter>
+
+ </web-app>
+
+Doing this also disables *Apache Isis* "exploration" actions (any action
+annotated with @Exploration will no longer be visible).
+
+Example Application
+===================
+
+> This appendix contains (almost) all the code that makes up the example
+> application shown in the screenshots in ?. The purpose in including
+> these listings is just to give you an idea of what it takes to write a
+> *Wicket Objects* application; this isn't a full tutorial on what it
+> all means.
+
+If you're interested in trying out the application, you'll find it at
+<https://wicketobjects.svn.sourceforge.net/svnroot/wicketobjects/trunk/testapp/claims>.
+
+Domain Application (Problem Space / Persisted Objects)
+------------------------------------------------------
+
+Most of the application shown in the screenshots (see ?) requires only
+the domain model. This is made up of three main entities, Employee,
+Claim and ClaimItem. The dependency between employee and claims package
+is acyclic; every Claim has a Claimant and an Approver, and Employee
+implements both the Approver and Claimant interfaces.
+
+### claims package
+
+#### Claim
+
+The Claim class is by far the largest domain class. Below is a listing
+of all the methods; the body of the getters and setters and some of the
+validation methods have been omitted.
+
+ package org.apache.isis.examples.claims.dom.claim;
+
+ import java.util.ArrayList;
+ import java.util.List;
+
+ import org.apache.isis.applib.AbstractDomainObject;
+ import org.apache.isis.applib.annotation.Disabled;
+ import org.apache.isis.applib.annotation.Ignore;
+ import org.apache.isis.applib.annotation.MaxLength;
+ import org.apache.isis.applib.annotation.MemberOrder;
+ import org.apache.isis.applib.annotation.Named;
+ import org.apache.isis.applib.annotation.Optional;
+ import org.apache.isis.applib.value.Date;
+ import org.apache.isis.applib.value.Money;
+ import org.apache.isis.viewer.wicket.applib.CalendarEvent;
+ import org.apache.isis.viewer.wicket.applib.Calendarable;
+
+ public class Claim extends AbstractDomainObject implements Calendarable {
+
+ // {{ Title
+ public String title() {
+ return getStatus() + " - " + getDate();
+ }
+ // }}
+
+ // {{ Lifecycle
+ public void created() {
+ status = "New";
+ date = new Date();
+ }
+ // }}
+
+ // {{ Rush
+ private boolean rush;
+ @MemberOrder(sequence = "1.2")
+ public boolean getRush() { ... }
+ public void setRush(final boolean flag) { ... }
+ // }}
+
+ // {{ Description
+ private String description;
+ @MemberOrder(sequence = "1")
+ public String getDescription() { ... }
+ public void setDescription(String description) { ... }
+ public String validateDescription(final String description) { ... }
+ // }}
+
+ // {{ Date
+ private Date date;
+ @MemberOrder(sequence = "2")
+ public Date getDate() { ... }
+ public void setDate(Date date) { ... }
+ public String disableDate() { ... }
+ // }}
+
+ // {{ Status
+ private String status;
+ @Disabled
+ @MemberOrder(sequence = "3")
+ @MaxLength(5)
+ public String getStatus() { ... }
+ public void setStatus(String status) { ... }
+ // }}
+
+ // {{ Claimant
+ private Claimant claimant;
+ @Disabled
+ @MemberOrder(sequence = "4")
+ public Claimant getClaimant() { ... }
+ public void setClaimant(Claimant claimant) { ... }
+ // }}
+
+ // {{ Approver
+ private Approver approver;
+ @MemberOrder(sequence = "5")
+ @Optional
+ public Approver getApprover() { ... }
+ public void setApprover(Approver approver) { ... }
+ public String disableApprover() { ... }
+ public String validateApprover(final Approver approver) {
+ if (approver == null)
+ return null;
+ return approver == getClaimant() ? "Can't approve own claims" : null;
+ }
+ // }}
+
+ // {{ Items
+ private List<ClaimItem> items = new ArrayList<ClaimItem>();
+ @MemberOrder(sequence = "6")
+ public List<ClaimItem> getItems() { ... }
+ public void addToItems(ClaimItem item) { ... }
+ // }}
+
+ // {{ action: Submit
+ public void submit(Approver approver) { ... }
+ public String disableSubmit() {
+ return getStatus().equals("New") ? null
+ : "Claim has already been submitted";
+ }
+ public Object default0Submit() {
+ return getClaimant().getApprover();
+ }
+ // }}
+
+ // {{ action: addItem
+ public void addItem(@Named("Days since") int days,
+ @Named("Amount") double amount,
+ @Named("Description") String description) {
+ ClaimItem claimItem = newTransientInstance(ClaimItem.class);
+ Date date = new Date();
+ date = date.add(0, 0, days);
+ claimItem.setDateIncurred(date);
+ claimItem.setDescription(description);
+ claimItem.setAmount(new Money(amount, "USD"));
+ persist(claimItem);
+ addToItems(claimItem);
+ }
+ public String disableAddItem() { ... }
+ return "Submitted".equals(getStatus()) ? "Already submitted" : null;
+ }
+ // }}
+
+ // object-level validation
+ public String validate() { ... }
+ }
+
+Some points worth noting:
+
+- Although Claim is inheriting from *Apache Isis*'
+ AbstractDomainObject class, this isn't mandatory.
+
+- Claim has reference properties of type Claimant and Approver. As
+ we'll see below these are interfaces. References to both interface
+ and classes is supported in *Apache Isis*.
+
+- The Claim uses a Money class, a value type provided by *Apache
+ Isis*. It's also possible to write ones own value types (or indeed
+ use third-party value types such as JodaTime).
+
+#### ClaimItem
+
+A Claim has a collection of ClaimItems. A ClaimItem is somewhat simpler
+than Claim, and doesn't have any particular behavior itself:
+
+ package org.apache.isis.examples.claims.dom.claim;
+
+ import org.apache.isis.applib.AbstractDomainObject;
+ import org.apache.isis.applib.annotation.MemberOrder;
+ import org.apache.isis.applib.value.Date;
+ import org.apache.isis.applib.value.Money;
+
+ public class ClaimItem extends AbstractDomainObject {
+
+ // {{ Title
+ public String title() {
+ return getDescription();
+ }
+ // }}
+
+ // {{ DateIncurred
+ private Date dateIncurred;
+ @MemberOrder(sequence = "1")
+ public Date getDateIncurred() { ... }
+ public void setDateIncurred(Date dateIncurred) { ... }
+ // }}
+
+ // {{ Description
+ private String description;
+ @MemberOrder(sequence = "2")
+ public String getDescription() { ... }
+ public void setDescription(String description) { ... }
+ // }}
+
+ // {{ Amount
+ private Money amount;
+ @MemberOrder(sequence = "3")
+ public Money getAmount() { ... }
+ public void setAmount(Money price) { ... }
+ // }}
+ }
+
+#### Approver and Claimant
+
+The Approver and Claimant interfaces decouple Claim from any classes
+outside the claims package. The Approver interface is, in fact, empty:
+
+ package org.apache.isis.examples.claims.dom.claim;
+
+ public interface Approver {
+
+ }
+
+There's not a lot more to Claimant:
+
+ package org.apache.isis.examples.claims.dom.claim;
+
+ public interface Claimant {
+
+ Approver getApprover();
+
+ String title();
+ }
+
+#### ClaimRepository
+
+The ClaimRepository interface is one of the two domain services (as
+appearing in the menu bar), and is defined as:
+
+ package org.apache.isis.examples.claims.dom.claim;
+
+ import java.util.List;
+
+ import org.apache.isis.applib.annotation.Named;
+ import org.apache.isis.applib.value.Date;
+
+ @Named("Claims")
+ public interface ClaimRepository {
+
+ public List<Claim> allClaims();
+
+ public List<Claim> findClaims(@Named("Description") String description);
+
+ public List<Claim> claimsFor(Claimant claimant);
+
+ public List<Claim> claimsSince(Claimant claimant, Date since);
+
+ public ClaimWizard newClaim(Claimant claimant);
+
+ public List<ClaimantExpenseSummary> analyseClaimantExpenses();
+ }
+
+### employee package
+
+The employee package depends on the claim package in that the Employee
+class implements the Claimant and Approver interfaces. Among other
+things, this allows the actions of the ClaimRepository to be
+"contributed" to the Employee class (appear in a "claims" submenu for
+each Employee).
+
+#### Employee
+
+The Employee class is the other main class in this app:
+
+ package org.apache.isis.examples.claims.dom.employee;
+
+ import org.apache.isis.applib.AbstractDomainObject;
+ import org.apache.isis.applib.annotation.Disabled;
+ import org.apache.isis.applib.annotation.MemberOrder;
+ import org.apache.isis.examples.claims.dom.claim.Approver;
+ import org.apache.isis.examples.claims.dom.claim.Claimant;
+ import org.apache.isis.viewer.wicket.applib.Locatable;
+ import org.apache.isis.viewer.wicket.applib.Location;
+
+ public class Employee extends AbstractDomainObject implements Claimant,
+ Approver, Locatable {
+
+ // {{ Title
+ public String title() {
+ return getName();
+ }
+
+ // }}
+
+ // {{ Icon
+ public String iconName() {
+ return getName().replaceAll(" ", "");
+ }
+ // }}
+
+ // {{ Name
+ private String name;
+ @MemberOrder(sequence = "1")
+ public String getName() { ... }
+ public void setName(String lastName) { ... }
+ // }}
+
+ // {{ Approver
+ private Approver approver;
+ @MemberOrder(sequence = "2")
+ public Approver getApprover() { ... }
+ public void setApprover(Approver approver) { ... }
+ // }}
+
+ // {{ Location
+ private Location location;
+ @Disabled
+ @MemberOrder(sequence = "1")
+ public Location getLocation() { ... }
+ public void setLocation(final Location location) { ... }
+ // }}
+ }
+
+A couple points worth noting:
+
+- The Employee class has an iconName() method. This is used to render
+ Employees with a customized image for each instance.
+
+- Employee also implements Locatable. This is used to render the
+ Employee in the gmap2 (google maps mashup) view (see ?).
+
+#### EmployeeRepository
+
+The EmployeeRepository interface defines the other domain service (on
+the services menu):
+
+ package org.apache.isis.examples.claims.dom.employee;
+
+ import java.util.List;
+
+ import org.apache.isis.applib.annotation.Named;
+
+ @Named("Employees")
+ public interface EmployeeRepository {
+
+ public List<Employee> allEmployees();
+ public List<Employee> findEmployees(@Named("Name") String name);
+ }
+
+Specialized Use Cases
+---------------------
+
+Domain objects to support specialized use cases (solution space objects)
+are not persisted; instead their state is serialized into the *Wicket*
+page components.
+
+### ClaimWizard
+
+The ClaimWizard uses an internal `page` field (of type Page enum) to
+determine which page the user is on; from this we determine which
+properties should be visible, and whether the `previous()`, `next()` and
+`finish()` actions are available.
+
+ package org.apache.isis.examples.claims.dom.claim;
+
+ import java.util.Calendar;
+ import java.util.List;
+
+ import org.apache.isis.applib.AbstractDomainObject;
+ import org.apache.isis.applib.annotation.Disabled;
+ import org.apache.isis.applib.annotation.Hidden;
+ import org.apache.isis.applib.annotation.Ignore;
+ import org.apache.isis.applib.annotation.MemberOrder;
+ import org.apache.isis.applib.annotation.NotPersistable;
+ import org.apache.isis.applib.annotation.TypicalLength;
+ import org.apache.isis.applib.clock.Clock;
+ import org.apache.isis.examples.claims.dom.employee.EmployeeRepository;
+ import org.apache.isis.viewer.wicket.applib.WizardPageDescription;
+
+ @NotPersistable
+ public class ClaimWizard extends AbstractDomainObject {
+
+ public enum Page {
+ INTRO("This wizard will take you through the process of creating a claim"),
+ CLAIMANT("Enter the claimant that is making this claim"),
+ APPROVER("By default, the claimant's own approver will approve this claim. " +
+ "Update here if another approver will approve this claim."),
+ DESCRIPTION("Update the description if required."),
+ SUMMARY("Confirm all details, or go back and amend if needed");
+
+ private String description;
+ private Page(String description) {
+ this.description = description;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public boolean hasPrevious() {
+ return ordinal() > 0;
+ }
+ public Page previous() {
+ if (hasPrevious()) {
+ return values()[ordinal() - 1];
+ } else {
+ return this;
+ }
+ }
+
+ public boolean hasNext() {
+ return ordinal() < values().length - 1;
+ }
+ public Page next() {
+ if (hasNext()) {
+ return values()[ordinal() + 1];
+ } else {
+ return this;
+ }
+ }
+
+ @Ignore
+ public boolean is(Page... pages) {
+ for (Page page : pages) {
+ if (page == this) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+
<TRUNCATED>