Skip to content
Edvard Fonsell edited this page Aug 13, 2019 · 19 revisions

Spring Boot is a popular way for creating stand-alone Spring applications. This page contains instructions for setting up nFlow on top of vanilla Spring Boot project using Gradle.

Bare minimum example

The bare minimum includes the nFlow engine only configuration for Spring Boot. The database is in-memory H2. The full working example can be found from nFlow Github repository.

  1. Generate Spring Boot application using Initializr. Use Gradle and add JDBC and H2 dependencies.

  2. Enable nflow.db.h2 profile and disable verbose logging by adding the following lines to src/main/resources/application.properties:

spring.profiles.active=nflow.db.h2
logging.level.root=WARN
  1. Add nflow-engine dependency to build.gradle dependencies:
compile("io.nflow:nflow-engine:5.7.0")
  1. Import nFlow engine configuration in Spring Boot application class:
@Import(EngineConfiguration.class)
  1. Create an example workflow that increments and outputs a counter every 10 seconds.

  2. Build the Spring Boot application in command line:

./gradlew build
  1. Start the Spring Boot application and observe the counter value is printed every 10 seconds on each ExampleWorkflow step:
java -jar build/libs/bare-minimum-0.0.1-SNAPSHOT.jar

Full stack example

The full stack includes the nFlow engine, REST API and Explorer user interface for Spring Boot. The database is in-memory H2. The full working example can be found from nFlow Github repository.

  1. Generate Spring Boot application using Initializr. Use Gradle and add web, Jersey, JDBC and H2 dependencies.

  2. Enable nflow.db.h2 profile, disable verbose logging and define context path for Jersey resources by adding the following lines to src/main/resources/application.properties:

spring.profiles.active=nflow.db.h2
logging.level.root=WARN
spring.jersey.application-path=/rest
  1. Add nflow-rest-api to build.gradle dependencies:
compile("io.nflow:nflow-rest-api:5.7.0")
  1. Import nFlow engine configuration in Spring Boot application class
@Import(RestConfiguration.class)
  1. Create an example workflow that increments and outputs a counter every 10 seconds.

  2. nFlow uses internally two Jackson ObjectMappers. Spring Boot needs to know the preferred ObjectMapper although Spring Boot is not used in this example for any JSON serialization. Just make one of the ObjectMappers primary so that Spring Boot is happy.

@Bean
@Primary
public ObjectMapper springWebObjectMapper(@NFlow ObjectMapper nflowObjectMapper) {
  return nflowObjectMapper;
}
  1. nFlow REST API is JAX-RS compatible module, so setup Jersey and register nFlow REST endpoints. Note: Jersey package scanning does not work for Spring Boot custom uberjar packaging, so resources need to be registered explicitly.
@Bean
public JerseyResourceConfig jerseyResourceConfig() {
  return new JerseyResourceConfig();
}

private static class JerseyResourceConfig extends ResourceConfig {
  public JerseyResourceConfig() {
    register(ArchiveResource.class);
    register(WorkflowDefinitionResource.class);
    register(WorkflowExecutorResource.class);
    register(WorkflowInstanceResource.class);
    register(StatisticsResource.class);
    register(DateTimeParamConverterProvider.class);
    register(JacksonFeature.class);
  }
}
  1. Download and package nFlow Explorer static resources inside Spring Boot uberjar by adding the following configuration to build.gradle:
plugins {
  id 'de.undercouch.download' version '3.1.1'
}

configurations {
  nflowExplorer
}

// add to dependencies
nflowExplorer group: 'io.nflow', name: 'nflow-explorer', version: '5.7.0', ext: 'tar.gz'

task resolveNflowExplorer(type: Copy) {
  destinationDir = file("$buildDir/resources/main/static/explorer")
  from { tarTree(resources.gzip(configurations.nflowExplorer.singleFile)) }
}

processResources.dependsOn resolveNflowExplorer
  1. In addition to static resources, nFlow Explorer needs to know the address and configuration of the nFlow REST API. Add nFlow Explorer configuration by creating a file src/main/resources/static/explorer/config.js.
var Config = function () {
  this.nflowEndpoints = [
    {
      id: 'Full stack example',
      title: 'Full stack example API',
      apiUrl: '/rest'
    }
  ];

  this.radiator = {
    // poll period in seconds
    pollPeriod: 15,
    // max number of items to keep in memory
    maxHistorySize: 10000
  };
};

  1. Build the Spring Boot application in command line
./gradlew build
  1. Start the Spring Boot application and observe the counter value is printed every 10 seconds on each ExampleWorkflow step. You can also open up local nFlow Explorer user interface.
java -jar build/libs/full-stack-0.0.1-SNAPSHOT.jar

Create an example workflow

  1. Copy & paste the following workflow definition to a new class file ExampleWorkflow
import org.joda.time.DateTime;

import io.nflow.engine.workflow.definition.NextAction;
import io.nflow.engine.workflow.definition.StateExecution;
import io.nflow.engine.workflow.definition.WorkflowDefinition;
import io.nflow.engine.workflow.definition.WorkflowStateType;

import static io.nflow.engine.workflow.definition.WorkflowStateType.manual;
import static io.nflow.engine.workflow.definition.WorkflowStateType.start;

public class ExampleWorkflow extends WorkflowDefinition<ExampleWorkflow.State> {

  public static final String TYPE = "repeatingWorkflow";
  public static final String VAR_COUNTER = "VAR_COUNTER";

  public enum State implements io.nflow.engine.workflow.definition.WorkflowState {
    repeat(start, "Repeating state"),
    error(manual, "Error state");

    private WorkflowStateType type;
    private String description;

    State(WorkflowStateType type, String description) {
      this.type = type;
      this.description = description;
    }

    @Override
    public WorkflowStateType getType() {
      return type;
    }

    @Override
    public String getDescription() {
      return description;
    }
  }

  public ExampleWorkflow() {
    super(TYPE, State.repeat, State.error);
    permit(State.repeat, State.repeat);
  }

  public NextAction repeat(StateExecution execution) {
    System.out.println("Counter: " + execution.getVariable(VAR_COUNTER));
    execution.setVariable(VAR_COUNTER, execution.getVariable(VAR_COUNTER, Integer.class) + 1);
    return NextAction.moveToStateAfter(State.repeat, DateTime.now().plusSeconds(10), "Next iteration");
  }
}
  1. Add an ExampleWorkflow bean to your Spring Boot application:
@Bean
public ExampleWorkflow exampleWorkflow() {
  return new ExampleWorkflow();
}
  1. Create a new ExampleWorkflow instance on application startup by using WorkflowInstanceFactory and WorkflowInstanceService in the Spring Boot application
@Inject
private WorkflowInstanceService workflowInstances;

@Inject
private WorkflowInstanceFactory workflowInstanceFactory;

@PostConstruct
public void createExampleWorkflowInstance() {
  workflowInstances.insertWorkflowInstance(workflowInstanceFactory.newWorkflowInstanceBuilder()
      .setType(ExampleWorkflow.TYPE)
      .setExternalId("example")
      .putStateVariable(ExampleWorkflow.VAR_COUNTER, 0)
      .build());
}