New version 0.13.0!

v0.13.0

Introduction

Hello World Application

This guide will walk you through building a "Hello World" workflow from scratch, covering these steps:

  • Creating a project
  • Writing tasks
  • Writing a workflow
  • Deploying workers
  • Starting a workflow

Our HelloWorld workflow will take a name string as input and return "Hello $name!", utilizing two tasks run on distributed workers:

  • A sayHello task that inputs a name string and outputs "Hello $name"
  • An addEnthusiasm task that inputs a str string and outputs "$str!"

Prerequisites

Before we begin, ensure you have Gradle installed, along with:

With Docker installed, you can set up the environment using the provided docker-compose.yml file:

services:
  # Pulsar settings
  pulsar-standalone:
    image: apachepulsar/pulsar:2.11.2
    environment:
      - BOOKIE_MEM=" -Xms512m -Xmx512m -XX:MaxDirectMemorySize=1g"
    command: >
      /bin/bash -c "bin/apply-config-from-env.py conf/standalone.conf && bin/pulsar standalone"
    volumes:
      - "pulsardata:/pulsar/data"
      - "pulsarconf:/pulsar/conf"
    ports:
      - "6650:6650"
      - "8080:8080"
      - "8081:8081"

  # Redis storage for state persistence
  redis:
    image: redis:6.0-alpine
    ports:
      - "6379:6379"
    volumes:
      - "redisdata:/data"

volumes:
  pulsardata:
  pulsarconf:
  redisdata:

Create project

Start by creating a new project:

mkdir hello-world && cd hello-world && gradle init

Configure this project:

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 2

Select implementation language:
  1: C++
  2: Groovy
  3: Java
  4: Kotlin
  5: Swift
Enter selection (default: Java) [1..5] 3

Split functionality across multiple subprojects?:
  1: no - only one application project
  2: yes - application and library projects
Enter selection (default: no - only one application project) [1..2] 1

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Kotlin) [1..2] 1

Project name (default: hello-world):
Source package (default: hello.world):

in our build gradle file, we'll include:

  • The Maven repository
  • The required dependencies
  • A directive to compile using Java 1.8"

And install dependencies:

./gradlew install

Writing services

Create a services directory:

mkdir app/src/main/java/hello/world/services

and define the HelloWorldService interface:

and its implementation, HelloWorldServiceImpl, which will contain our tasks:

Writing workflow

Set up a workflows directory:

mkdir app/src/main/java/hello/world/workflows

and add the HelloWorldWorkflow interface:

and its HelloWorldWorkflowImpl implementation:

This implementation must extend io.infinitic.workflows.Workflow

Note: the newService function creates a stub from the HelloWorldService interface.

Syntax-wise, this stub functions like an implementation of HelloWorldService. However, instead of executing a method directly, it sends a message to carry out the execution. This is why running a workflow without deploying any workers will result in no action being taken.

Pulsar configuration

Configure Pulsar in the app/infinitic.yml file:

pulsar:
  brokerServiceUrl: pulsar://localhost:6650
  webServiceUrl: http://localhost:8080
  tenant: infinitic
  namespace: dev

Deploying workers

Set up services and workflows, and update values for Redis and Pulsar connections as necessary:

storage:
  redis:
    host: localhost
    port: 6379
    user:
    password:
    database: 0

pulsar:
  brokerServiceUrl: pulsar://localhost:6650
  tenant: infinitic
  namespace: dev

services:
  - name: hello.world.services.HelloWorldService
    class: hello.world.services.HelloWorldServiceImpl

workflows:
  - name: hello.world.workflows.HelloWorld
    class: hello.world.workflows.HelloWorldImpl

Replace the App file with:

Our app is ready to run as a worker:

./gradlew run

We have a working worker listening Pulsar and waiting for instructions:

> Task :run
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

The SLF4J output messages appear because our app doesn't have a logger set up yet. To eliminate these messages, we can add a logger of our choice, such as Simple Logger, as a dependency in our Gradle build file.

When making code changes, it's necessary to restart the workers to ensure they incorporate these updates.

Start a workflow

Use a config file with pulsar configuration to instantiate an InfiniticClient. Use this client to create a workflow stub and dispatch the workflow.

Here, we already have the infinitic.yml file that we can reuse:

We can run this directly from our IDE (remembering to possibly adjust the working directory in the Run configuration), or we can add a startWorkflow Gradle task to our build file:

and run it from the command line:

./gradlew startWorkflow --args Infinitic

Where the app/worker is running, we should see:

Hello Infinitic!

Congrats! You have run your first Infinitic workflows.

Debugging

Check-list

In case of issues, check:

  • If Pulsar and Redis are running
  • Correctness of infinitic.yml file that
    • should expose correct values to access Pulsar and Redis
    • should have name and class that match interface names and implementation full names respectively of our task and workflows
  • If at least one worker is running

Keep in mind that workers will continue running even if an exception occurs in our tasks or workflows. To observe these exceptions, you need to set up a logger and then review the log file for any errors.

Simple logger

To use SimpleLogger as logger in this app, just add the dependency in our Gradle build file:

and this simplelogger.properties example file in our resources directory:

# SLF4J's SimpleLogger configuration file
# Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err.

# Uncomment this line to use a log file
#org.slf4j.simpleLogger.logFile=infinitic.log

# Default logging detail level for all instances of SimpleLogger.
# Must be one of ("trace", "debug", "info", "warn", or "error").
# If not specified, defaults to "info".
org.slf4j.simpleLogger.defaultLogLevel=warn

# Set to true if you want the current date and time to be included in output messages.
# Default is false, and will output the number of milliseconds elapsed since startup.
org.slf4j.simpleLogger.showDateTime=true

# Set to true if you want to output the current thread name.
# Defaults to true.
org.slf4j.simpleLogger.showThreadName=false


# Set to true if you want the last component of the name to be included in output messages.
# Defaults to false.
org.slf4j.simpleLogger.showShortLogName=true

Working repository

If we fail to chase a bug, we still can copy this working repository and look for the differences:

git clone https://github.com/infiniticio/infinitic-example-java-hello-world
Previous
Examples