Execution Outbox

The Execution Outbox module provides an implementation of ExecutionSubscriber that persists Executions into an "outbox" (using either the JPA/EclipseLink or JDO/DataNucleus object store). The purpose of the "outbox" is to act as a temporary store of executions to be propogated to other "downstream" systems.

The module also provides a REST API, a rest client and DTOs to represent the payloads between these two services. The client polls the outbox through the REST API (for example every 15 seconds), and uses it to download any new executions. Once processed (eg published to a message bus), the client then uses the same REST API to delete the executions. The usage REST client is described below.

Setup

Dependency Management

Add a section for Execution Outbox's own BOM:

pom.xml
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.apache.causeway.extensions</groupId>
            <artifactId>causeway-extensions-executionoutbox</artifactId>
            <scope>import</scope>
            <type>pom</type>
            <version>2.0.0</version>
        </dependency>
    </dependencies>
</dependencyManagement>

Dependencies / Imports

In the webapp module of your application, add the following dependency:

pom.xml
<dependencies>
    <dependency>
        <groupId>org.apache.causeway.extensions</groupId>
        <artifactId>causeway-extensions-executionoutbox-persistence-XXX</artifactId>    (1)
    </dependency>
</dependencies>
1 either:

In your application’s App Manifest, import the ExecutionOutbox modules. The exact modules to use will depend upon the persistence mechanism in use:

AppManifest.java
@Configuration
@Import({
        ...
        CausewayModuleExtExecutionOutboxPersistenceXxx.class,        (1)
        ...
})
public class AppManifest {
}
1 either:

If using SecMan, you will also need to import the Fixture module; SecMan uses fixture scripts to seed its entities.

Configuration Properties

Add the database schema used by the Execution Outbox entities to the configuration file:

application.yml
causeway:
  persistence:
    schema:
      auto-create-schemas: causewayExtExecutionOutbox

Optionally, modify the configuration properties for the Execution Outbox module itself:

application.yml
causeway:
  extensions:
    execution-outbox:
      rest-api:
        max-pending: 100

Programmatic usage (queueing up commands)

With the extension configured, any action (or property edit) that has execution publishing enabled will result in an ExecutionOutboxEntry being persisted. The REST Client (described below) can then be set up to poll for and subsequent remove these entries.

Often the action in question should be a no-op; all that is required is create an ExecutionOutboxEntry with the appropriate state. This state will include a bookmark of the target object (on which the action was invoked) along with a serialization of the parameters.

There are two options when publishing an action:

  • make the event as "thin" as possible, ie inform the downstream systems that a significant event has occurred, but no more.

    With this approach the downstream systems will probably make a RESTful API call to any additional information to perform their work

  • alternatively, provide supplemental information in the event, which may obviate the requirement for a REST call.

    This can be done by defining action that has String parameters that take XML or JSON; these can therefore publish as much information as is necessary to the downstream systems.

For example:

  • define a no-op action taking XML parameters.

    This should have publishing enabled, but we don’t want it visible in the UI so it should also be hidden:

    @Action(
            semantics = SemanticsOf.IDEMPOTENT,
            publishing = Publishing.ENABLED,
            hidden = Where.EVERYWHERE
    )
    public void addSupplier(
            final String addRequestXml,
            final String getRequestXml) {
        // no-op
    }
  • define a convenience method:

    @Programmatic
    public T toOutbox() {
        return (T)wrapperFactory.wrapSkipRules(this);
    }
  • queue up commands, eg:

    val addRequestXml = ...;
    val getRequestXml = ...;
    toOutbox().addSupplier(addRequestXml, getRequestXml);

User Interface

The extension provides a number of menu actions,that allow the administrator to query the persisted commands. These should be added to menu bar, and access to these restricted.

The sections below describe how.

Once configured, the extension provides a number of menu actions. You can use menubars.layout.xml to arrange these as you see fit. To get you started, the following fragment adds all of the actions to an "Activity" secondary menu:

menubars.layout.xml
<mb:secondary>
    ...
    <mb:menu>
        <mb:named>Activity</mb:named>
        ...
        <mb:section>
            <mb:named>Execution Outbox</mb:named>
            <mb:serviceAction id="findOldest" objectType="causeway.ext.executionOutbox.ExecutionOutboxMenu"/>
            <mb:serviceAction id="findAll" objectType="causeway.ext.executionOutbox.ExecutionOutboxMenu"/>
        </mb:section>
        ...
    </mb:menu>
</mb:secondary>

SecMan Security Roles

If SecMan extension is configured, then permissions must be granted to access the menu actions.

This can be done by granting the role set up by the CausewayExtExecutionOutboxRoleAndPermissions seed fixture script (see its ROLE_NAME constant).

Outbox REST Client

Once an execution has been persisted into the outbox, it will stay there until it has been processed and removed by another process. Typically that other process will be a microservice that forwards on the message to an event bus.

This is shown below.

outbox
Figure 1. processing messages from the outbox

The module provides a REST service, along with a rest client, OutboxClient. The OutboxClient is used by the message processor shown in the above diagram.

Prerequisites

To setup the message processor:

  • in dependencyManagement section, add an entry for Execution Outbox's own BOM:

    pom.xml
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.apache.causeway.extensions</groupId>
                <artifactId>causeway-extensions-executionoutbox</artifactId>
                <scope>import</scope>
                <type>pom</type>
                <version>2.0.0</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
  • In the dependencies section, add the following dependency:

    pom.xml
    <dependencies>
        <dependency>
            <groupId>org.apache.causeway.extensions</groupId>
            <artifactId>causeway-extensions-executionoutbox-restclient</artifactId>
        </dependency>
    </dependencies>

Usage

To instantiate the OutboxClient, specify the URL, user and password. The URL will be something like: http://localhost:8080/restful/, where the last part is the default path obtainable from the resteasy.jaxrs.defaultPath configuration property of the Causeway app.

The OutboxClient API consists of three methods:

  • to retrieve any pending interactions:

    List<InteractionDto> pending = outboxClient.pending();
  • to delete a single interaction:

    val first = pending.get(0);
    val interactionId = first.getInteractionId();
    val sequence      = first.getExecution().getSequence();
    
    outboxClient.delete(interactionId, sequence);
  • to delete many interactions:

    outboxClient.deleteMany(pending);

The maximum number of interactions that will be returned is configurable, see above.

See also