All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.camunda.zeebe.scheduler.Actor Maven / Gradle / Ivy

The newest version!
/*
 * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
 * one or more contributor license agreements. See the NOTICE file distributed
 * with this work for additional information regarding copyright ownership.
 * Licensed under the Camunda License 1.0. You may not use this file
 * except in compliance with the Camunda License 1.0.
 */
package io.camunda.zeebe.scheduler;

import io.camunda.zeebe.scheduler.future.ActorFuture;
import io.camunda.zeebe.util.Loggers;
import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

public abstract class Actor implements AutoCloseable, AsyncClosable, ConcurrencyControl {

  public static final String ACTOR_PROP_NAME = "actor-name";
  public static final String ACTOR_PROP_PARTITION_ID = "partitionId";

  private static final int MAX_CLOSE_TIMEOUT = 300;
  protected final ActorControl actor = new ActorControl(this);
  private Map context;

  /**
   * Should be overwritten by sub classes to add more context where the actor is run.
   *
   * @return the context of the actor
   */
  protected Map createContext() {
    // return an modifiable map in order to simplify sub class implementation
    final var baseContext = new HashMap();
    baseContext.put(ACTOR_PROP_NAME, getName());
    return baseContext;
  }

  public String getName() {
    return getClass().getSimpleName();
  }

  /**
   * @return a map which defines the context where the actor is run. Per default it just returns a
   *     map with the actor name. Ideally sub classes add more context, like the partition id etc.
   */
  public Map getContext() {
    if (context == null) {
      context = Collections.unmodifiableMap(createContext());
    }
    return context;
  }

  public boolean isActorClosed() {
    return actor.isClosed();
  }

  protected void onActorStarting() {
    // setup
  }

  protected void onActorStarted() {
    // logic
  }

  protected void onActorClosing() {
    // tear down
  }

  protected void onActorClosed() {
    // what ever
  }

  protected void onActorCloseRequested() {
    // notification that timers, conditions, etc. will no longer trigger from now on
  }

  public static Actor wrap(final Consumer r) {
    return new Actor() {
      @Override
      public String getName() {
        return r.toString();
      }

      @Override
      protected void onActorStarted() {
        r.accept(actor);
      }
    };
  }

  @Override
  public void close() {
    closeAsync().join(MAX_CLOSE_TIMEOUT, TimeUnit.SECONDS);
  }

  @Override
  public ActorFuture closeAsync() {
    return actor.close();
  }

  public static String buildActorName(final String name, final int partitionId) {
    return "%s-%d".formatted(name, partitionId);
  }

  /** Invoked when a task throws and the actor phase is not 'STARTING' and 'CLOSING'. */
  protected void handleFailure(final Throwable failure) {
    Loggers.ACTOR_LOGGER.error(
        "Uncaught exception in '{}' in phase '{}'. Continuing with next job.",
        getName(),
        actor.getLifecyclePhase(),
        failure);
  }

  public void onActorFailed() {
    // clean ups
  }

  @Override
  public  void runOnCompletion(
      final ActorFuture future, final BiConsumer callback) {
    actor.runOnCompletion(future, callback);
  }

  @Override
  public  void runOnCompletion(
      final Collection> actorFutures, final Consumer callback) {
    actor.runOnCompletion(actorFutures, callback);
  }

  @Override
  public void run(final Runnable action) {
    actor.run(action);
  }

  @Override
  public  ActorFuture call(final Callable callable) {
    return actor.call(callable);
  }

  @Override
  public ScheduledTimer schedule(final Duration delay, final Runnable runnable) {
    return actor.schedule(delay, runnable);
  }

  public static ActorBuilder newActor() {
    return new ActorBuilder();
  }

  public static class ActorBuilder {

    private String name;
    private Consumer actorStartedHandler;

    public ActorBuilder name(final String name) {
      this.name = name;
      return this;
    }

    public ActorBuilder actorStartedHandler(final Consumer actorStartedHandler) {
      this.actorStartedHandler = actorStartedHandler;
      return this;
    }

    public Actor build() {
      final var wrapper =
          new Consumer() {

            @Override
            public String toString() {
              if (name != null) {
                return name;
              } else if (actorStartedHandler != null) {
                return actorStartedHandler.getClass().getName();
              } else {
                return super.toString();
              }
            }

            @Override
            public void accept(final ActorControl t) {
              if (actorStartedHandler != null) {
                actorStartedHandler.accept(t);
              }
            }
          };

      return wrap(wrapper);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy