Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Warning

When using external flow please make sure that the host and the target runtimes have the same major and minor versions.

Introduction

The external flow is a concept that allows embedding and starting a flow from a different project, without the use of Portal Messages.

The value of this functionality is to get:

  • a good design of the solution:
    • Structure your application
    • Make your solution scalable
  • a good application UX:
    • The end user directly see which information is loaded and what not
  • a good application model:
    • Modelling is more explicit which brings clarity in the model

Conceptually it works as depicted in the following figure.

Image Removed

This container will replace the AQ_Dashboard_FlowWidget and AQ_Dashboard_ProjectWidget.

Definitions

Host RuntimeThe runtime which contains a project that has an AQ_ExternalFlow_ container implementation running.  Target RuntimeThe runtime which contains a project with a flow that can be started by an AQ_ExternalFlow_ container implementation. Source ProfileThe domain model of the source. Target ProfileThe domain model of the target.Interface ProfileThe domain model of the interfaceDatastore                                           The location where the serialized profile is stored.

Requirements

In order to use this functionality you need to enable Blueriq External Flow Component.

Limitations

  • Running the external flow using host and target runtimes with different major and minor versions is not supported.

Configuration

To configure the External Flow Container to work on the runtime you have to do the following:

  1. Enable external-flow profile in bootstrap.properties.
  2. Replace the material version (>=1.0.9)
Info

External flow functionality saves the profile in a Redis Datastore. Please consult Blueriq External Flow Component in order to see more details on how to configure the runtime to support the external flow.

Using other datastore implementation

Blueriq provides IExternalFlowStore interface which can be used to interact with a generic datastore. A default implementation is already provided in the Blueriq External Flow Component. If other datastore implementation is desired the following have to be done:

  • Add the blueriq-component-api to the project dependencies.
Code Block
themeConfluence
titlepom.xml
linenumberstrue
collapsetrue
<project ...>
  ...  
  <dependencies>
    ...
    <dependency>
	  <groupId>com.blueriq</groupId>
	  <artifactId>blueriq-component-api</artifactId>
	  <version>${blueriq.version}</version>
    </dependency>
    ...
  </dependencies>
  ...
</project>
  • Define the custom implementation like below:
Code Block
languagejava
themeConfluence
titleCustomExternalFlowStore.java
linenumberstrue
collapsetrue
package ...;

import com.blueriq.component.api.store.externalflow.IExternalFlowStore;
// other imports ...

@Component
public class CustomExternalFlowStore implements IExternalFlowStore {

  @Override
  public void delete(String key) 
    throws ExternalFlowStoreException, IllegalArgumentException {
    // add implementation here
  }

  @Override
  public <T extends Serializable> T get(String key, Class<T> valueType) 
    throws ExternalFlowStoreException,    IllegalArgumentException {   
    // add implementation here
    return null; 
  }

  @Override
  public boolean hasKey(String key) 
    throws ExternalFlowStoreException, IllegalArgumentException {    
    // add implementation here
    return false;
  }

  @Override
  public void set(String key, Serializable value) 
    throws ExternalFlowStoreException, IllegalArgumentException {
    // add implementation here
  }
}

Hazelcast Datastore

Here is a Hazelcast implementation of the datastore.

Adding the dependencies

The following dependencies are required in the pom.xml

Code Block
themeConfluence
titlepom.xml
linenumberstrue
collapsetrue
<project ...>
  ...
  <dependencies>
    ...
    <dependency>
      <groupId>com.hazelcast</groupId>
      <artifactId>hazelcast-all</artifactId>
      <version>${hazelcast.version}</version>
    </dependency>
    <dependency>
      <groupId>com.blueriq</groupId>
      <artifactId>blueriq-component-api</artifactId>
      <version>${blueriq.version}</version>
    </dependency>
    ...
  <dependencies>
  ...
</project>

Implementing the IExternalFlowStore Interface

Warning
iconfalse

Please make sure that HazelcastExternalFlowStore component is scanned by Spring.

Code Block
languagejava
themeConfluence
titleHazelcastExternalFlowStore.java
linenumberstrue
collapsetrue
package ...;

import com.blueriq.component.api.store.externalflow.IExternalFlowStore;
// other imports ...

@Component
public class HazelcastExternalFlowStore implements IExternalFlowStore {

  private final IMap<String, Serializable> map;

  public HazelcastExternalFlowStore() {
    ClientConfig config = new ClientConfig();
    // configure Hazelcast properties here
    HazelcastInstance hazelcastInstance
      = HazelcastClient.newHazelcastClient(config);
    map = hazelcastInstance.getMap("blueriq");
  }

  @Override
  public void delete(String key) 
    throws ExternalFlowStoreException, IllegalArgumentException {
    if (isInvalid(key)) {
      throw new IllegalArgumentException("Invalid key.");
    }

    try {
      map.delete(key);
    } catch (Exception e) {
      throw new ExternalFlowStoreException("Something went wrong", e);
    }
  }

  @Override
  public <T extends Serializable> T get(String key, Class<T> valueType)
      throws ExternalFlowStoreException, IllegalArgumentException {
    if (isInvalid(key)) {
      throw new IllegalArgumentException("Invalid key.");
    }

    try {
      Serializable storedValue = map.get(key);
      if (storedValue == null) {
        return null;
      }

      if (valueType.isInstance(storedValue)) {
        return valueType.cast(storedValue);
      }

      throw new IllegalArgumentException(
          String.format("Cannot cast from %s to %s.", 
            storedValue.getClass(), valueType));
    } catch (Exception e) {
      throw new ExternalFlowStoreException("Something went wrong", e);
    }
  }

  @Override
  public boolean hasKey(String key) 
    throws ExternalFlowStoreException, IllegalArgumentException {
    if (isInvalid(key)) {
      throw new IllegalArgumentException("Invalid key.");
    }

    try {
      return map.get(key) != null;
    } catch (Exception e) {
      throw new ExternalFlowStoreException("Something went wrong", e);
    }
  }

  @Override
  public void set(String key, Serializable value) 
    throws ExternalFlowStoreException, IllegalArgumentException {
    if (isInvalid(key)) {
      throw new IllegalArgumentException("Invalid key.");
    }

    try {
      map.set(key, value, 1, TimeUnit.MINUTES);
    } catch (Exception e) {
      throw new ExternalFlowStoreException("Something went wrong", e);
    }

  }

  private boolean isInvalid(String key) {
    // define what an invalid key means
    return false;
  }

}

Modeling an External Flow

Todo

External Flow Endpoints

Init external flow 

Code Block
languagejs
POST /api/v2/session/{sessionId}/externalflow/init

{"configurationId": "{configurationId}}"

Description

Initialises the external container component on the HOST runtime.

Parameters

URL SegmentExpected TypeDescription
sessionIdAquimaSessionSession for which the external container was expanded.

The body must be a JSON containing the configuration ID of type string

Example Response

If successful, status 200 OK and : 

Code Block
{
	"configurationId" : "123-456-789"
}

Start external flow 

Code Block
languagejs
POST /api/v2/externalflow/start

{"configurationId": "{configurationId}}"

Description

Starts the external flow container on the TARGET runtime.

Parameters

A JSON containing the configuration ID of type string.

Example Response

If successful, status 200 OK and : 

Code Block
{
	"sessionId" : "123-456-789"
}

Complete external flow 

Code Block
languagejs
POST /api/v2/session/{sessionId}/externalflow/{configurationId}/complete

Description

Completes the external flow and executes the mapping between the interface and source profile.

Parameters

URL SegmentExpected TypeDescription
sessionIdAquimaSessionThe Id of the AquimaSession from the host runtime.  
configurationIdstringUnique id for the external container configuration.

Example Response

If successful, status 200 OK and : 

Code Block
{
   "events": [
      {
       	"sessionId": <sessionId>,
     	"changes": {"changes": []},
      	"type": "page"
      }, 
	  ...
	]
}

End external flow 

Code Block
languagejs
POST /api/v2/session/{sessionId}/externalflow/{configurationId}/end

Description

Closes the external session

Parameters

URL SegmentExpected TypeDescription
sessionIdAquimaSessionThe Id of the AquimaSession from the target runtime.  
configurationIdstringUnique id for the external container configuration.

Response

If successful, status code 200 OK

Known Issues

Todo ???

The

Table of Contents