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 2015/06/10 09:50:24 UTC

isis git commit: ISIS-1133: EventBusService, GuiceBeanProvider, working on IsisJdoSupport.

Repository: isis
Updated Branches:
  refs/heads/master d3f640e65 -> 9b44b9a2d


ISIS-1133: EventBusService, GuiceBeanProvider, working on IsisJdoSupport.


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/9b44b9a2
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/9b44b9a2
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/9b44b9a2

Branch: refs/heads/master
Commit: 9b44b9a2dd5b71a1b0de9163fbeffbe7ec4b8854
Parents: d3f640e
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Wed Jun 10 08:49:48 2015 +0100
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Wed Jun 10 08:49:48 2015 +0100

----------------------------------------------------------------------
 adocs/documentation/monitor.rb                  |   2 +
 ...nObjectContainer_generic-repository-api.adoc |   5 +
 ...ce-services-api_manpage-EventBusService.adoc | 371 +++++++++++++------
 ...-services-api_manpage-GuiceBeanProvider.adoc |  68 +++-
 ...nce-services-api_manpage-IsisJdoSupport.adoc | 182 +++++++--
 ...-services-spi_manpage-PublishingService.adoc |   2 +-
 .../_ug_runtime_configuring-core.adoc           |  13 +
 7 files changed, 497 insertions(+), 146 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/9b44b9a2/adocs/documentation/monitor.rb
----------------------------------------------------------------------
diff --git a/adocs/documentation/monitor.rb b/adocs/documentation/monitor.rb
index f10d93f..df7b28a 100644
--- a/adocs/documentation/monitor.rb
+++ b/adocs/documentation/monitor.rb
@@ -87,6 +87,8 @@ def process(file,srcBasePath,targetBasePath,templateDir,i,lastTimeGenerated,prim
 	       timeUntilNext > 0 then
 	        puts "skipping before regenerating (3 seconds not yet elapsed)"
 	    else
+		# wait 1 further second for any additional edits
+	    	sleep 1
                 cmd = "asciidoctor #{regenerate} --require asciidoctor-diagram --backend html --eruby erb --template-dir '#{templateDir}' --destination-dir='#{targetRelDir}' -a imagesdir='' -a toc=right -a icons=font -a source-highlighter=coderay"
 
                 unless priming then

http://git-wip-us.apache.org/repos/asf/isis/blob/9b44b9a2/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-api_manpage-DomainObjectContainer_generic-repository-api.adoc
----------------------------------------------------------------------
diff --git a/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-api_manpage-DomainObjectContainer_generic-repository-api.adoc b/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-api_manpage-DomainObjectContainer_generic-repository-api.adoc
index 59c23cc..c646c3b 100644
--- a/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-api_manpage-DomainObjectContainer_generic-repository-api.adoc
+++ b/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-api_manpage-DomainObjectContainer_generic-repository-api.adoc
@@ -82,3 +82,8 @@ public class ToDoItemRepositoryImplUsingJdoql implements ToDoItemRepositoryImpl
 Other JDOQL named queries (not shown) follow the exact same pattern.
 
 With respect to the other query APIs, the varargs parameters are optional, but allow for (client-side and managed) paging.  The first parameter is the `start` (0-based, the second is the `count`.
+
+[TIP]
+====
+It is also possible to query using DataNucleus' type-safe query API.  For more details, see xref:_ug_reference-services-api_manpage-IsisJdoSupport[`IsisJdoSupport`].
+====

http://git-wip-us.apache.org/repos/asf/isis/blob/9b44b9a2/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-api_manpage-EventBusService.adoc
----------------------------------------------------------------------
diff --git a/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-api_manpage-EventBusService.adoc b/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-api_manpage-EventBusService.adoc
index bdab0d6..14fc820 100644
--- a/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-api_manpage-EventBusService.adoc
+++ b/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-api_manpage-EventBusService.adoc
@@ -4,226 +4,358 @@
 :_basedir: ../
 :_imagesdir: images/
 
-IMPORTANT: TODO
 
-## Implementation
 
-IMPORTANT: TODO
+The `EventBusService` allows domain objects to emit events to subscribing domain services using an in-memory event bus.
 
-## Registration
+The primary user of the service is the framework itself, which automatically emit events for xref:_ug_reference-annotations_manpage-Action_domainEvent[actions], xref:_ug_reference-annotations_manpage-Property_domainEvent[properties] and xref:_ug_reference-annotations_manpage-Collection_domainEvent[collections].  Multiple events are generated:
 
-The `EventBusServiceJdo` class provides the default implementation and (as of 1.6.0+) is automatically registered using `@DomainService`.  No further configuration is necessary.
+* when an object member is to be viewed, an event is fired; subscribers can veto (meaning that the member is hidden)
+* when an object member is to be enabled, the same event instance is fired; subscribers can veto (meaning that the member is disabled, ie cannot be edited/invoked)
+* when an object member is being validated, then a new event instance is fired; subscribers can veto (meaning that the candidate values/action arguments are rejected)
+* when an object member is about to be changed, then the same event instance is fired; subscribers can perform pre-execution operations
+* when an object member has been changed, then the same event instance is fired; subscribers can perform post-execution operations
 
+If a subscriber throws an exception in the first three steps, then the interaction is vetoed. If a subscriber throws an exception in the last two steps, then the transaction is aborted.  For more on this topic, see xref:_ug_reference-annotations_manpage-Action_domainEvent[`@Action#domainEvent()`], xref:_ug_reference-annotations_manpage-Property_domainEvent[`@Property#domainEvent()`] and xref:_ug_reference-annotations_manpage-Collection_domainEvent[`@Collection#domainEvent()`].
 
+It is also possible for domain objects to programmatically generate domain events.  However the events are published, the primary use case is to decoupling interactions from one module/package/namespace and another.
 
+Two implementations are available, using either link:https://code.google.com/p/guava-libraries/[Guava]'s https://code.google.com/p/guava-libraries/wiki/EventBusExplained[`EventBus`], or alternatively using the link:http://www.axonframework.org/[AxonFramework]'s link:http://www.axonframework.org/docs/2.4/single.html#d5e1489[SimpleEventBus].  It is also possible to plug in a custom implementation.
 
-The `EventBusService` is a wrapper for a https://code.google.com/p/guava-libraries/[Guava] https://code.google.com/p/guava-libraries/wiki/EventBusExplained[`EventBus`], allowing arbitrary events to be posted and subscribed to (within the Isis runtime). The primary use case is for decoupling interactions from one module/package/namespace and another.
 
-The framework also provides an implementation of this service (`EventBusServiceJdo`), and this is automatically registered (it is annotated with `@DomainService`).
 
-Domain events are raised automatically whenever any domain object is interacted with. Domain events can also be raised programmatically if required (though there is rarely any need to do so).
+[[_ug_reference-services-api_manpage-EventBusService_api-and-implementation]]
+== API & Implementation
 
-Only domain services should be registered as subscribers; not domain entities. Domain services are guaranteed to be instantiated and resident in memory, whereas the same is not true of domain entities.
+The API defined by `EventBusService` is:
 
-The typical implementation of a domain service subscriber is to identify the impacted entities, load them using a repository query, and then to delegate to the event to them.
+[source,java]
+----
+public abstract class EventBusService {
+    @Programmatic
+    public void post(Object event) { ... }                          // <1>
+    @Programmatic
+    public void register(final Object domainService) { ... }        // <2>
+    @Programmatic
+    public void unregister(final Object domainService) { ... }      // <3>
+}
+----
+<1> posts the event onto event bus
+<2> allows domain services to register themselves.  This should be done in their xref:_ug_reference-annotations_manpage-PostConstruct[`@PostConstruct`] initialization method (for both singleton and xref:_ug_reference-annotations_manpage-RequestScoped[`@RequestScoped`] domain services.
+<3> exists for symmetry, but need never be called (it is in fact deliberately a no-op).
 
+Isis provides a default implementation of the service, `o.a.i.objectstore.jdo.datanucleus.service.eventbus.EventBusServiceJdo`.
 
 
-== Publishing
 
 
-=== Automatically published events
 
-Domain events are raised automatically whenever an object member (property, collection or action) is interacted with. Specifically:
+== Registering Subscribers
 
-* when an object member is to be viewed, an event is fired; subscribers can veto (meaning that the member is hidden)
-* when an object member is to be enabled, the same event instance is fired; subscribers can veto (meaning that the member is disabled, ie cannot be edited/invoked)
-* when an object member is being validated, then a new event instance is fired; subscribers can veto (meaning that the candidate values/action arguments are rejected)
-* when an object member is about to be changed, then the same event instance is fired; subscribers can perform pre-execution operations
-* when an object member has been changed, then the same event instance is fired; subscribers can perform post-execution operations
+The `register()` method should be called in the xref:_ug_reference-annotations_manpage-PostConstruct[`@PostConstruct`] lifecycle method.  It is valid and probably the least confusing to readers to also "unregister" in the xref:_ug_reference-annotations_manpage-PreDestroy[`@PreDestroy`] lifecycle method (though as noted xref:_ug_reference-services-api_manpage-EventBusService_api-and-implementation[above], unregistering is actually a no-op).
 
-If a subscriber throws an exception in the first three steps, then the interaction is vetoed. If a subscriber throws an exception in the last two steps, then the transaction is aborted.
+For example:
 
-The event class that is raised can be specified using an annotation:
+[source,java]
+----
+@DomainService(nature=NatureOfService.DOMAIN)
+public class MySubscribingDomainService {
+    @PostConstruct
+    public void postConstruct() {
+        eventBusService.register(this);
+    }
+    @PreDestroy
+    public void preDestroy() {
+        eventBusService.unregister(this);
+    }
+    ...
+    @javax.inject.Inject
+    EventBusService eventBusService;
+}
+----
 
-*
+This works for both singleton (application-scoped) and also xref:_ug_reference-annotations_manpage-RequestScoped[`@RequestScoped`] domain services.
 
-as of 1.8.0, using:
 
-** `@Property(domainEvent=...)`
-** `@Collection(domainEvent=...)`
-** `@Action(domainEvent=...)`
-*
 
-as of 1.7.0 (but deprecated in 1.8.0), using :
 
-** `@PropertyInteraction`
-** `@CollectionInteraction`
-** `@ActionInteraction`
-*
 
-as of 1.6.0 (but deprecated in 1.8.0), using :
+== Annotating Members
 
-** `@PostsPropertyChangedEvent`
-** `@PostsCollectionAddedToEvent`/`@PostsCollectionRemovedFromEvent`
-** `@PostsActionInvokedEvent`
+As discussed in the introduction, the framework will automatically emit domain events for all of the object members (actions, properties or collections) of an object whenever that object is rendered or (more generally) interacted with.
 
-For example, in the Isis addons example https://github.com/isisaddons/isis-app-todoapp/[todoapp] (not ASF) a custom event is raised when an item is completed:
+For example:
 
 [source,java]
 ----
-public static class CompletedEvent extends AbstractActionDomainEvent {
-    private static final long serialVersionUID = 1L;
-    public CompletedEvent(
-            final ToDoItem source,
-            final Identifier identifier,
-            final Object... arguments) {
-        super("completed", source, identifier, arguments);
-    }
+public class Customer {
+    @Action
+    public Customer placeOrder(Product product, @ParameterLayout(named="Quantity") int qty) { ... }
+    ...
 }
-
- @Action(
-         domainEvent =CompletedEvent.class,
-         ...
- )
- public ToDoItem completed() { ... }
 ----
 
-Subscribers can therefore subscribe to these subclasses, in effect filtering out only the events that are of interest.
+will propagate an instance of the default `o.a.i.applib.services.eventbus.ActionDomainEvent.Default` class.   If using the Guava event bus this can be subscribed to using:
 
-=== Explicitly publishing events
+[source,java]
+----
+@DomainService(nature=NatureOfService.DOMAIN)
+public class MySubscribingDomainService
+    @Programmatic
+    @com.google.common.eventbus.Subscribe
+    public void on(ActionDomainEvent ev) { ... }
+    ...
+}
+----
 
-The publishing API defined by `EventBusService` is:
+or if using Axonframework, the subscriber uses a different annotation:
 
 [source,java]
 ----
-public abstract class EventBusService {
-    public void post(Object event) { ... }
+@DomainService(nature=NatureOfService.DOMAIN)
+public class MySubscribingDomainService
+    @Programmatic
+    @org.axonframework.eventhandling.annotation.EventHandle
+    public void on(ActionDomainEvent ev) { ... }
     ...
 }
 ----
 
-where
+More commonly though you will probably want to emit domain events of a specific subtype.  As a slightly more interesting example, suppose in a library domain that a `LibraryMember` wants to leave the library. A letter should be sent out detailing any books that they still have out on loan:
 
-* `post(Object)` posts the event onto event bus (it is simply a wrapper that delegates to `EventBus#post(Object)`)
+In the `LibraryMember` class, we publish the event by way of an annotation:
 
-== Subscribing
+[source,java]
+----
+public class LibraryMember {
+    @Action(domainEvent=LibraryMemberLeaveEvent.class)  // <1>
+    public void leave() { ... }
+    ...
+}
+----
+<1> `LibraryMemberLeaveEvent` is a subclass of `o.a.i.applib.eventbus.ActionDomainEvent`.  The topic of subclassing is discussed in more detail xref:_ug_reference-services-api_manpage-EventBusService_event-hierarchy[below].
 
-The subscribing API defined by `EventBusService` is:
+Meanwhile, in the `BookRepository` domain service, we subscribe to the event and act upon it.  For example:
 
 [source,java]
 ----
-public abstract class EventBusService {
-    public void post(Object event) { ... }
+public class BookRepository {
+    @Programmatic
+    @com.google.common.eventbus.Subscribe
+    public void onLibraryMemberLeaving(LibraryMemberLeaveEvent e) {
+        LibraryMember lm = e.getLibraryMember();
+        List<Book> lentBooks = findBooksOnLoanFor(lm);
+        if(!lentBooks.isEmpty()) {
+            sendLetter(lm, lentBooks);
+        }
+    }
     ...
 }
 ----
 
-where:
-* `register(Object)` registers the object with the event bus (it is simply a wrapper that delegates to `EventBus#register(Object)`)
-* `unregister(Object)` unregisters the object with the event bus (it is simply a wrapper that delegates to `EventBus#unregister(Object)`)
+This design allows the `libraryMember` module to be decoupled from the `book` module.
+
+
+
+[[_ug_reference-services-api_manpage-EventBusService_event-hierarchy]]
+== Event hierarchy
 
-The are for domain services to call to register themselves with the event bus. This should be done in the
+By creating domain event subtypes we can be more semantically precise and in turn providesmore flexibility for subscribers: they can choose whether to be broadly applicable (by subscribing to a superclass) or to be tightly focussed (by subscribing to a subclass).
 
-As of 1.8.0, the `register` and `unregister` methods should be called in the `@PostConstruct` and `@PreDestroy` lifecycle methods:
+We recommend that you define event classes at (up to) four scopes:
+
+* at the top "global" scope is the Isis-defined `o.a.i.applib.event.ActionDomainEvent`
+* for the "module" scope, create a static class to represent the module itself, and creating nested classes within
+* for each "class" scope, create a nested static event class in the domain object's class for all of the domain object's actions
+* for each "action" scope, create a nested static event class for that action, inheriting from the "domain object" class.
+
+To put all that into code; at the module level we can define:
 
 [source,java]
 ----
-@DomainService
-public class MySubscribingDomainService {
+package com.mycompany.modules.libmem;
+...
+public static class LibMemModule {
+    private LibMemModule() {}
+    public abstract static class ActionDomainEvent<S>
+                extends org.apache.isis.applib.event.ActionDomainEvent<S> {
+        private static final long serialVersionUID = 1L;
+        public ActionDomainEvent(
+            final S source,
+            final Identifier identifier,
+            final Object... arguments) {
+            super(source, identifier, arguments);
+        }
+    }
+    ...         // <1>
+}
+----
+<1> similar events for properties and collections should also be defined
 
-    ...
+For the class-level we can define:
 
-    @Programmatic
-    @PostConstruct
-    public void postConstruct() {
-        eventBusService.register(this);
-    }
-    @Programmatic
-    @PreDestroy
-    public void preDestroy() {
-        eventBusService.unregister(this);
+[source,java]
+----
+public static class LibraryMember {
+    public abstract static class ActionDomainEvent extends LibMemModule.ActionDomainEvent<LibraryMember> {
+        private static final long serialVersionUID = 1L;
+        public ActionDomainEvent(
+            final LibraryMember source,
+            final Identifier identifier,
+            final Object... arguments) {
+            super(source, identifier, arguments);
+        }
     }
-
-    @javax.inject.Inject
-    private EventBusService eventBusService;
+    ...         // <1>
 }
 ----
+<1> similar events for properties and collections should also be defined
 
-This works for both application-scoped and request-scoped (`@RequestScoped`) domain services.
-
-In 1.7.0, the registration should be done when the `EventBusService` is injected into it:
+and finally at the action level we can define:
 
 [source,java]
 ----
-@DomainService
-public class MySubscribingDomainService {
-
+public class LibraryMember {
     ...
-
-    private EventBusService eventBusService;
-    public void injectEventBusService(final EventBusService eventBusService) {
-        this.eventBusService = eventBusService;
-        eventBusService.register(this);
+    public static class LeaveEvent extends LibraryMember.ActionDomainEvent {
+        private static final long serialVersionUID = 1L;
+        public PlaceOrderEvent(
+            final Customer source,
+            final Identifier identifier,
+            final Object... arguments) {
+        super(source, identifier, arguments);
+        }
     }
+    @Action(domainEvent=LeaveEvent.class)
+    public void leave() { ... }
+    ...
 }
 ----
 
-=== Callbacks
+The subscriber can subscribe either to the general superclass (as before), or to any of the classes in the hierarchy.
 
-The actual callbacks of events is done by annotating a method on the class (that is, the usual Guava programming model).
 
-For example, the subscriber in the todoapp for completed todo items would use the following callback:
 
-[source,java]
-----
-@Programmatic
-@Subscribe
-public void on(final ToDoItem.CompletedEvent ev) { ... }
-----
 
-As a slightly more interesting example, suppose in a library domain that a `LibraryMember` wants to leave the library. A letter should be sent out detailing any books that they still have out on loan:
 
-In the `LibraryMember` class, we publish the event:
+== Programmatic posting
+
+To programmatically post an event, simply call `#post()`.
+
+The `LibraryMember` example described above could for example be rewritten into:
 
 [source,java]
 ----
 public class LibraryMember {
     ...
-    @Action(domainEvent=LibraryMemberLeaveEvent.class)
     public void leave() {
         ...
+        eventBusService.post(new LibraryMember.LeaveEvent(...));    // <1>
     }
     ...
 }
 ----
+<1> `LibraryMember.LeaveEvent` could be _any_ class, not just a subclass of `o.a.i.applib.event.ActionDomainEvent`.
+
+In practice we suspect there will be few cases where the programmatic approach is required rather than the declarative approach afforded by xref:_ug_reference-annotations_manpage-Action_domainEvent[`@Action#domainEvent()`] et al.
+
+
+
+== Using `WrapperFactory`
+
+An alternative way to cause events to be posted is through the xref:_ug_reference-services-api_manpage-WrapperFactory[`WrapperFactory`].  This is useful when you wish to enforce a (lack-of-) trust boundary between the caller and the callee.
+
+For example, suppose that `Customer#placeOrder(...)` emits a `PlaceOrderEvent`, which is subscribed to by a `ReserveStockSubscriber`.  This subscriber in turn calls `StockManagementService#reserveStock(...)`.  Any business rules on `#reserveStock(...)` should be enforced.
 
-Meanwhile, in the `BookRepository` domain service, we subscribe to the event and act upon it:
+In the `ReserveStockSubscriber`, we therefore use the `WrapperFactory`:
 
 [source,java]
 ----
-public class BookRepository {
-
-    @com.google.common.eventbus.Subscribe
+@DomainService(nature=NatureOfService.DOMAIN)
+public class ReserveStockSubscriber {
     @Programmatic
-    public void onLibraryMemberLeaving(LibraryMemberLeaveEvent e) {
-        LibraryMember lm = e.getLibraryMember();
-        List<Book> lentBooks = findBooksOnLoanFor(lm);
-        if(!lentBooks.isEmpty()) {
-            sendLetter(lm, lentBooks);
-        }
+    @Subscribe
+    public void on(Customer.PlaceOrderEvent ev) {
+        wrapperFactory.wrap(stockManagementService)
+                      .reserveStock(ev.getProduct(), ev.getQuantity());
     }
     ...
+    @Inject
+    StockManagementService stockManagementService;
+    @Inject
+    WrapperFactory wrapperFactory;
 }
 ----
 
-This design allows the `libraryMember` module to be decoupled from the `book` module.
 
+== Implementation SPI
 
+The implementation of `EventBusService` provided by Isis will by default use link:https://code.google.com/p/guava-libraries/[Guava]'s https://code.google.com/p/guava-libraries/wiki/EventBusExplained[`EventBus`] as the underlying in-memory event bus.  Alternatively the link:http://www.axonframework.org/[AxonFramework]'s link:http://www.axonframework.org/docs/2.4/single.html#d5e1489[SimpleEventBus] can be used.  Which is used is specified through configuration property (described xref:_ug_reference-services-api_manpage-EventBusService_Configuration[below]).
+
+[NOTE]
+.Guava vs Axon, which to use?
+====
+Guava actually queues up events; they are not guaranteed to be dispatched immediately.  This generally is not problem, but can be for cases where the subscriber may in turn want to post its own events (using xref:_ug_reference-services-api_manpage-WrapperFactory[`WrapperFactory`]).
+
+The Axon `SimpleEventBus`-based implementation on the other hand is fully synchronous; events are dispatched as soon as they are posted.  This works well in all scenarios (that we have tested).
+====
+
+
+It is also possible to use some other implementation.
+
+[source,java]
+----
+public interface EventBusImplementation {
+    void register(Object domainService);
+    void unregister(Object domainService);
+    void post(Object event);
+}
+----
+
+As is probably obvious, the `EventBusService` just delegates down to these method calls when its own similarly named methods are called.
+
+If you do provide your own implementation of this SPI, be aware that your subscribers will need to use whatever convention is required (eg different annotations) such that the events are correctly routed through to your subscribers.
+
+
+
+[[_ug_reference-services-api_manpage-EventBusService_Configuration]]
+== Configuration
+
+The implementation of `EventBusService` provided by Isis will by default use link:https://code.google.com/p/guava-libraries/[Guava]'s https://code.google.com/p/guava-libraries/wiki/EventBusExplained[`EventBus`] as the underlying in-memory event bus.  Alternatively the link:http://www.axonframework.org/[AxonFramework]'s link:http://www.axonframework.org/docs/2.4/single.html#d5e1489[SimpleEventBus] can be used.
+
+To specify which, add the xref:_ug_runtime_configuring-core[configuration property] `isis.services.eventbus.implementation`:
+
+[source,ini]
+----
+isis.services.eventbus.implementation=guava
+----
+
+or
+
+[source,ini]
+----
+isis.services.eventbus.implementation=axon
+----
+
+If you have written your own implementation of the `EventBusServiceImplementation` SPI, then specify instead its fully-qualified class name:
+
+[source,ini]
+----
+isis.services.eventbus.implementation=com.mycompany.isis.MyEventBusServiceImplementation
+----
+
+
+
+In addition, there is one further configuration property, whether to allow "late registration":
+
+[source,ini]
+----
+isis.services.eventbus.allowLateRegistration=false
+----
+
+Late registration refers to the idea that a domain service can register itself with the `EventBusService` after events have been posted.  Since domain services are set up at boot time, this almost certainly constitutes a bug in the code and so by default late registration is _not_ allowed.  Setting the above property to `true` disables this check.
 
-== Implementation
 
-Isis provides a default implementation of the service, `org.apache.isis.objectstore.jdo.datanucleus.service.eventbus.EventBusServiceJdo`.
 
 
 
@@ -239,8 +371,11 @@ isis.services-installer=configuration-and-annotation
 then Isis core' implementation of `EventBusService` service is automatically registered and injected (it is annotated with `@DomainService`) so no further configuration is required.
 
 
+
+
+
 == Related Services
 
-The `EventBusService` is intended for fine-grained publish/subscribe for object-to-object interactions within an Isis domain object model. The event propogation is strictly in-memory, and there are no restrictions on the object acting as the event (it need not be serializable, for example).
+The `EventBusService` is intended for fine-grained publish/subscribe for object-to-object interactions within an Isis domain object model. The event propagation is strictly in-memory, and there are no restrictions on the object acting as the event (it need not be serializable, for example).
 
 The xref:_ug_reference-services-spi_manpage-PublishingService[`PublishingService`] meanwhile is intended for coarse-grained publish/subscribe for system-to-system interactions, from Isis to some other system. Here the only events published are those that action invocations (for actions annotated with xref:_ug_reference-annotations_manpage-Action_publishing[`@Action#publishing()`]) and of changed objects (for objects annotated with xref:_ug_reference-annotations_manpage-DomainObject_publishing[`@DomainObject#publishing()`]).
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/9b44b9a2/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-api_manpage-GuiceBeanProvider.adoc
----------------------------------------------------------------------
diff --git a/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-api_manpage-GuiceBeanProvider.adoc b/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-api_manpage-GuiceBeanProvider.adoc
index 40d9e9d..d1f665e 100644
--- a/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-api_manpage-GuiceBeanProvider.adoc
+++ b/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-api_manpage-GuiceBeanProvider.adoc
@@ -4,10 +4,76 @@
 :_basedir: ../
 :_imagesdir: images/
 
-IMPORTANT: TODO
 
+The `GuiceBeanProvider` domain service acts as a bridge between Isis' xref:_ug_wicket-viewer[Wicket viewer] internal bootstrapping using link:https://github.com/google/guice[Google Guice].
 
+This service operates at a very low-level, and you are unlikely to have a need for it.  It is used internally by the framework, in the default implementation of the xref:_ug_reference-services-api_manpage-DeepLinkService[`DeepLinkService`].
 
+[NOTE]
+====
+Currently Isis uses a combination of Guice (within the Wicket viewer only) and a home-grown dependency injection framework.  In future versions we intended to refactor the framework to use CDI throughout.  At that time this service is likely to become redundant because we will allow any of the internal components of Isis to be injected into your domain object code.
+====
+
+
+== API & Implementation
+
+The API defined by this service is:
+
+[source,java]
+----
+public interface GuiceBeanProvider {
+    @Programmatic
+    <T> T lookup(Class<T> beanType);
+    @Programmatic
+    <T> T lookup(Class<T> beanType, final Annotation qualifier);
+}
+----
+
+The xref:_ug_wicket-viewer[Wicket viewer] this provides an implementation of this service.
+
+
+
+== Usage
+
+Using the xref:_ug_wicket-viewer[Wicket viewer] requires subclassing of `IsisWicketApplication`.  In the subclass it is commonplace to override `newIsisWicketModule()`, for example:
+
+[source,java]
+----
+@Override
+protected Module newIsisWicketModule() {
+    final Module isisDefaults = super.newIsisWicketModule();
+    final Module overrides = new AbstractModule() {
+        @Override
+        protected void configure() {
+            bind(String.class).annotatedWith(Names.named("applicationName"))
+                              .toInstance("ToDo App");
+            bind(String.class).annotatedWith(Names.named("applicationCss"))
+                              .toInstance("css/application.css");
+            bind(String.class).annotatedWith(Names.named("applicationJs"))
+                              .toInstance("scripts/application.js");
+            ...
+        }
+    };
+    return Modules.override(isisDefaults).with(overrides);
+}
+----
+
+This "module" is in fact a Guice module, and so the `GuiceBeanProvider` service can be used to lookup any of the components bound into it.
+
+For example:
+
+[source,java]
+----
+public class SomeDomainObject {
+    private String lookupApplicationName() {
+        return guiceBeanProvider.lookup(String.class, Names.named("applicationName"));
+    }
+    @Inject
+    GuiceBeanProvider guiceBeanProvider;
+}
+----
+
+should return "ToDo App".
 
 
 

http://git-wip-us.apache.org/repos/asf/isis/blob/9b44b9a2/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-api_manpage-IsisJdoSupport.adoc
----------------------------------------------------------------------
diff --git a/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-api_manpage-IsisJdoSupport.adoc b/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-api_manpage-IsisJdoSupport.adoc
index 1098378..e7a51e8 100644
--- a/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-api_manpage-IsisJdoSupport.adoc
+++ b/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-api_manpage-IsisJdoSupport.adoc
@@ -5,31 +5,29 @@
 :_imagesdir: images/
 
 
-The `IsisJdoSupport` service provides a number of general purpose methods for working with the JDO/DataNucleus objectstore.
+The `IsisJdoSupport` service provides a number of general purpose methods for working with the JDO/DataNucleus objectstore.  In general these act at a lower-level of abstraction than the APIs normally used (specifically, those of xref:_ug_reference-services-api_manpage-DomainObjectContainer[`DomainObjectContainer`]), but nevertheless deal with some of the most common use cases.  For service also provides access to the underlying JDO `PersistenceManager` for full control.
 
+The following sections discuss the functionality provided by the service, broken out into categories.
 
-== API
 
-The API provided by `IsisJdoSupport` is:
+
+== Executing SQL
+
+You can use the `IsisJdoSupportService` to perform arbitrary SQL SELECTs or UPDATEs:
 
 [source,java]
 ----
 public interface IsisJdoSupport {
+    @Programmatic
     List<Map<String, Object>> executeSql(String sql);
+    @Programmatic
     Integer executeUpdate(String sql);
-    void deleteAll(Class<?>... pcClasses);
-    <T> T refresh(T domainObject);
-    void ensureLoaded(Collection<?> collectionOfDomainObjects);
-    PersistenceManager getJdoPersistenceManager();
+    ...
 }
 ----
 
-Each of these methods are discussed in more details below.
-
 
-== Executing arbitrary SQL
-
-The `executeSql(...)` method allows arbitrary queries to be submitted:
+The `executeSql(...)` method allows arbitrary SQL `SELECT` queries to be submitted:
 
 [source,java]
 ----
@@ -39,7 +37,7 @@ List<Map<String, Object>> results = isisJdoSupport.executeSql("select * from cus
 The result set is automatically converted into a list of maps, where the map key is the column name.
 
 
-In a similar manner, the `executeUpdate(...)` allows arbitrary updates to be performed.
+In a similar manner, the `executeUpdate(...)` allows arbitrary SQL ``UPDATE``s to be performed.
 
 [source,java]
 ----
@@ -48,17 +46,117 @@ int count = isisJdoSupport.executeUpdate("select count(*) from custMgmt.customer
 
 The returned value is the number of rows updated.
 
+[TIP]
+====
+As an alternative, consider using DataNucleus' link:http://www.datanucleus.org/products/accessplatform_4_0/jdo/jdoql_typesafe.html[type-safe JDO query API], discussed xref:_ug_reference-services-api_manpage-IsisJdoSupport_type-safe-query-api[below].
+====
+
+
+
+
+[[_ug_reference-services-api_manpage-IsisJdoSupport_type-safe-jdoql-queries]]
+== Using type-safe JDOQL Queries
+
+DataNucleus provides an link:http://www.datanucleus.org/products/accessplatform_4_0/jdo/jdoql_typesafe.html[extension to JDO],  so that JDOQL queries can be built up and executed using a set of type-safe classes.
+
+The types in question for type safe queries are not the domain entities, but rather are companion "Q..." query classes.  These classes are generated dynamically by an link:https://www.jcp.org/en/jsr/detail?id=269[annotation processor] as a side-effect of compilation, one "Q..." class for each of the xref:_ug_reference-annotations_manpage-PersistenceCapable[`@PersistenceCapable`] domain entity in your application.  For example, a `ToDoItem` domain entity will give rise to a `QToDoItem` query class.  These "Q..." classes mirror the structure of domain entity, but expose properties that allow predicates to be built up for querying instances, as well as other functions in support of order by. group by and other clauses.
+
+[NOTE]
+====
+Most IDEs (including IntelliJ and Eclipse) enable annotation processing by default, as does Maven.  The DataNucleus' link:http://www.datanucleus.org/products/accessplatform_4_0/jdo/jdoql_typesafe.html[documentation] offers some guidance on confirming that APT is enabled.
+====
+
+The `IsisJdoSupport` service offers two methods at different levels of abstraction:
+
+[source,java]
+----
+public interface IsisJdoSupport {
+    @Programmatic
+    <T> List<T> executeQuery(final Class<T> cls, final BooleanExpression be);
+    @Programmatic
+    <T> TypesafeQuery<T> newTypesafeQuery(Class<T> cls);
+    ...
+}
+----
+
+
+The `executeQuery(...)` method supports the common case of obtaining a set of objects that meet some criteria, filtered using the provided `BooleanExpression`.  To avoid memory leaks, the returned list is cloned and the underlying query closed.
+
+For example, in the (non-ASF) http://github.com/isisaddons/isis-app-todoapp[Isis addons' todoapp] there is an implementation of `ToDoItemRepository` using type-safe queries.  The following JDOQL:
+
+[source,sql]
+----
+SELECT
+FROM todoapp.dom.module.todoitem.ToDoItem
+WHERE atPath.indexOf(:atPath) == 0
+   && complete == :complete")
+----
+
+can be expressed using type-safe queries as follows:
+
+[source,java]
+----
+public List<ToDoItem> findByAtPathAndCategory(final String atPath, final Category category) {
+    final QToDoItem q = QToDoItem.candidate();
+    return isisJdoSupport.executeQuery(ToDoItem.class,
+            q.atPath.eq(atPath).and(
+            q.category.eq(category)));
+}
+----
+
+[NOTE]
+====
+You can find the full example of the JDOQL equivalent in the xref:_ug_reference-services-api_manpage-DomainObjectContainer_generic-repository-api[`DomainObjectContainer`]
+====
+
+
+The `newTypesafeQuery(...)` method is a lower-level API that allows a type safe query to be instantiated for most sophisticated querying, eg using group by or order by clauses.  See the
+DataNucleus link:http://www.datanucleus.org/products/accessplatform_4_0/jdo/jdoql_typesafe.html[documentation] for full details of using this.
+
+One thing to be aware of is that after the query has been executed, it should be closed, using `query.closeAll()`.  If calling `query.executeList()` we also recommend cloning the resultant list first.  The following utility method does both of these tasks:
+
+[source,java]
+----
+private static <T> List<T> executeListAndClose(final TypesafeQuery<T> query) {
+    final List<T> elements = query.executeList();
+    final List<T> list = Lists.newArrayList(elements);
+    query.closeAll();
+    return list;
+}
+----
+
+
+
 
 
 == Fixture support
 
 The `deleteAll(...)` method is provided pretty much exclusively for tearing down fixture data:
 
+
 [source,java]
 ----
-deleteAll(Order.class);
-deleteAll(CustomerAddress.class);
-deleteAll(Customer.class);
+public interface IsisJdoSupport {
+    @Programmatic
+    void deleteAll(Class<?>... pcClasses);
+    ...
+}
+----
+
+
+
+[source,java]
+----
+public class TearDownAll extends FixtureScriptAbstract {
+    @Override
+    protected void execute(final ExecutionContext ec) {
+        isisJdoSupport.deleteAll(Order.class);
+        isisJdoSupport.deleteAll(CustomerAddress.class);
+        isisJdoSupport.deleteAll(Customer.class);
+    }
+    @Inject
+    IsisJdoSupport isisJdoSupport;
+}
 ----
 
 [NOTE]
@@ -66,7 +164,7 @@ deleteAll(Customer.class);
 It can occasionally be the case that Isis' internal adapter for the domain object is
 still in memory.  JDO/DataNucleus seems to bump up the version of the object prior to its deletion,
 which under normal circumstances would cause Isis to throw a concurrency exception.  Therefore
-to prevent this from happening (ie to <i>force</i> the deletion of all instances), concurrency checking
+to prevent this from happening (ie to _force_ the deletion of all instances), concurrency checking
 is temporarily disabled while this method is performed.
 ====
 
@@ -74,7 +172,21 @@ is temporarily disabled while this method is performed.
 
 == Reloading entities
 
-A http://www.datanucleus.org/products/datanucleus/jdo/orm/relationships.html[known limitation] of DataNucleus' implementation of JDO is that persisting a child entity (in a 1:n bidirectional relationship) does not cause the parent's collection to be updated.
+An http://www.datanucleus.org/products/datanucleus/jdo/orm/relationships.html[(intentional) limitation] of JDO/DataNucleus is that persisting a child entity (in a 1:n bidirectional relationship) does not cause the parent's collection to be updated.
+
+[source,java]
+----
+public interface IsisJdoSupport {
+    @Programmatic
+    <T> T refresh(T domainObject);                              // <1>
+    @Programmatic
+    void ensureLoaded(Collection<?> collectionOfDomainObjects); // <2>
+    ...
+}
+----
+<1> force a reload of a domain object (using the JDO `PersistenceManager#refresh(...)` API).
+<2> force the loading of a collection of domain objects (using the JDO `PersistenceManager#retrieveAll(...)` API)
+
 
 The `refresh(T domainObject)` method can be used to reload the parent object (or indeed any object).
 
@@ -82,13 +194,20 @@ For example:
 
 [source,java]
 ----
-public Order newOrder(final Customer customer) {
-    Order order = newTransientInstance(Order.class);
-    order.setCustomer(customer);
-    persist(customer);
-    getContainer().flush(); // <1>
-    isisJdoSupport.refresh(customer); // <2>
-    return order;
+@DomainService(nature=NatureOfService.VIEW_CONTRIBUTIONS_ONLY)
+public class OrderContributions {
+    public Order newOrder(final Customer customer) {
+        Order order = newTransientInstance(Order.class);
+        order.setCustomer(customer);
+        container.persist(customer);
+        container.flush();                  // <1>
+        isisJdoSupport.refresh(customer);   // <2>
+        return order;
+    }
+    @Inject
+    DomainObjectContainer container;
+    @Inject
+    IsisJdoSupport isisJdoSupport;
 }
 ----
 <1> flush to database
@@ -110,7 +229,18 @@ refresh the parent.
 
 == Using JDO `PersistenceManager` directly
 
-Apache Isis provides a simplified API on top of only supports JDO, and so does not support all of JDO's features.  For example, Isis (currently) only supports named queries.  If you require more flexibility than this, eg for dynamically constructed queries, then the service provides access to the underlying JDO `PersistenceManager` API:
+The functionality provided by `IsisJdoSupport` focus only on the most common use cases.  If you require more flexibility than this, eg for dynamically constructed queries, then you can use the service to access the underlying JDO `PersistenceManager` API:
+
+[source,java]
+----
+public interface IsisJdoSupport {
+    @Programmatic
+    PersistenceManager getJdoPersistenceManager();
+    ...
+}
+----
+
+For example:
 
 [source,java]
 ----

http://git-wip-us.apache.org/repos/asf/isis/blob/9b44b9a2/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-spi_manpage-PublishingService.adoc
----------------------------------------------------------------------
diff --git a/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-spi_manpage-PublishingService.adoc b/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-spi_manpage-PublishingService.adoc
index 7ebef9a..604e086 100644
--- a/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-spi_manpage-PublishingService.adoc
+++ b/adocs/documentation/src/main/asciidoc/user-guide/_ug_reference-services-spi_manpage-PublishingService.adoc
@@ -246,7 +246,7 @@ It also provides an implementation of the `EventSerializer` that (using API expo
 
 The `PublishingService` is intended for coarse-grained publish/subscribe for system-to-system interactions, from Isis to some other system. Here the only events published are those that action invocations (for actions annotated with xref:_ug_reference-annotations_manpage-Action_publishing[`@Action#publishing()`]) and of changed objects (for objects annotated with xref:_ug_reference-annotations_manpage-DomainObject_publishing[`@DomainObject#publishing()`].
 
-The xref:_ug_reference-services-api_manpage-EventBusService[`EventBusService`] meanwhile is intended for fine-grained publish/subscribe for object-to-object interactions within an Isis domain object model. The event propogation is strictly in-memory, and there are no restrictions on the object acting as the event (it need not be serializable, for example).
+The xref:_ug_reference-services-api_manpage-EventBusService[`EventBusService`] meanwhile is intended for fine-grained publish/subscribe for object-to-object interactions within an Isis domain object model. The event propagation is strictly in-memory, and there are no restrictions on the object acting as the event (it need not be serializable, for example).
 
 
 

http://git-wip-us.apache.org/repos/asf/isis/blob/9b44b9a2/adocs/documentation/src/main/asciidoc/user-guide/_ug_runtime_configuring-core.adoc
----------------------------------------------------------------------
diff --git a/adocs/documentation/src/main/asciidoc/user-guide/_ug_runtime_configuring-core.adoc b/adocs/documentation/src/main/asciidoc/user-guide/_ug_runtime_configuring-core.adoc
index a11a41a..d506db3 100644
--- a/adocs/documentation/src/main/asciidoc/user-guide/_ug_runtime_configuring-core.adoc
+++ b/adocs/documentation/src/main/asciidoc/user-guide/_ug_runtime_configuring-core.adoc
@@ -142,6 +142,19 @@ NB: note that the key is mis-spelt, (`isis.service.email` rather than `isis.serv
 NB: note that the key is mis-spelt, (`isis.service.email` rather than `isis.services.email`)
 
 
+| `isis.services.` +
+`eventbus.implementation` +
+| `guava`, `axon`, FQCN (`_guava_`)
+|which implementation to use as the underlying event bus.
+
+| `isis.services.` +
+`eventbus.allowLateRegistration` +
+| `true`, `false`, (`_false_`)
+|whether a domain service can register after any events have posted. +
+
+Since this almost certainly constitutes a bug in application code, by default this is disallowed.
+
+
 |`isis.services.` +
 `publish.objects`
 | `all`, `none` (`all`)