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

io.dapr.actors.runtime.ActorRuntime Maven / Gradle / Ivy

There is a newer version: 1.13.0-rc-1
Show newest version
/*
 * Copyright 2021 The Dapr Authors
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
limitations under the License.
*/

package io.dapr.actors.runtime;

import io.dapr.actors.ActorId;
import io.dapr.actors.ActorTrace;
import io.dapr.client.DaprApiProtocol;
import io.dapr.client.DaprHttpBuilder;
import io.dapr.config.Properties;
import io.dapr.serializer.DaprObjectSerializer;
import io.dapr.serializer.DefaultObjectSerializer;
import io.dapr.utils.Version;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import reactor.core.publisher.Mono;

import java.io.Closeable;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * Contains methods to register actor types. Registering the types allows the
 * runtime to create instances of the actor.
 */
public class ActorRuntime implements Closeable {

  /**
   * Serializer for internal Dapr objects.
   */
  private static final ActorObjectSerializer INTERNAL_SERIALIZER = new ActorObjectSerializer();

  /**
   * A trace type used when logging.
   */
  private static final String TRACE_TYPE = "ActorRuntime";

  /**
   * Tracing errors, warnings and info logs.
   */
  private static final ActorTrace ACTOR_TRACE = new ActorTrace();

  /**
   * Gets an instance to the ActorRuntime. There is only 1.
   */
  private static volatile ActorRuntime instance;

  /**
   * Channel for communication with Dapr.
   */
  private final ManagedChannel channel;

  /**
   * Configuration for the Actor runtime.
   */
  private final ActorRuntimeConfig config;

  /**
   * A client used to communicate from the actor to the Dapr runtime.
   */
  private final DaprClient daprClient;

  /**
   * Map of ActorType --> ActorManager.
   */
  private final ConcurrentMap actorManagers;

  /**
   * The default constructor. This should not be called directly.
   *
   * @throws IllegalStateException If cannot instantiate Runtime.
   */
  private ActorRuntime() throws IllegalStateException {
    this(buildManagedChannel());
  }

  /**
   * Constructor once channel is available. This should not be called directly.
   *
   * @param channel GRPC managed channel to be closed (or null).
   * @throws IllegalStateException If cannot instantiate Runtime.
   */
  private ActorRuntime(ManagedChannel channel) throws IllegalStateException {
    this(channel, buildDaprClient(channel));
  }

  /**
   * Constructor with dependency injection, useful for testing. This should not be called directly.
   *
   * @param channel GRPC managed channel to be closed (or null).
   * @param daprClient Client to communicate with Dapr.
   * @throws IllegalStateException If class has one instance already.
   */
  private ActorRuntime(ManagedChannel channel, DaprClient daprClient) throws IllegalStateException {
    if (instance != null) {
      throw new IllegalStateException("ActorRuntime should only be constructed once");
    }

    this.config = new ActorRuntimeConfig();
    this.actorManagers = new ConcurrentHashMap<>();
    this.daprClient = daprClient;
    this.channel = channel;
  }

  /**
   * Returns an ActorRuntime object.
   *
   * @return An ActorRuntime object.
   */
  public static ActorRuntime getInstance() {
    if (instance == null) {
      synchronized (ActorRuntime.class) {
        if (instance == null) {
          instance = new ActorRuntime();
        }
      }
    }

    return instance;
  }

  /**
   * Gets the Actor configuration for this runtime.
   *
   * @return Actor configuration.
   */
  public ActorRuntimeConfig getConfig() {
    return this.config;
  }

  /**
   * Gets the Actor configuration for this runtime.
   *
   * @return Actor configuration serialized.
   * @throws IOException If cannot serialize config.
   */
  public byte[] serializeConfig() throws IOException {
    return INTERNAL_SERIALIZER.serialize(this.config);
  }

  /**
   * Registers an actor with the runtime, using {@link DefaultObjectSerializer} and {@link DefaultActorFactory}.
   *
   * {@link DefaultObjectSerializer} is not recommended for production scenarios.
   *
   * @param clazz            The type of actor.
   * @param               Actor class type.
   */
  public  void registerActor(Class clazz) {
    registerActor(clazz, new DefaultObjectSerializer(), new DefaultObjectSerializer());
  }

  /**
   * Registers an actor with the runtime, using {@link DefaultObjectSerializer}.
   *
   * {@link DefaultObjectSerializer} is not recommended for production scenarios.
   *
   * @param clazz            The type of actor.
   * @param actorFactory     An optional factory to create actors. This can be used for dependency injection.
   * @param               Actor class type.
   */
  public  void registerActor(Class clazz, ActorFactory actorFactory) {
    registerActor(clazz, actorFactory, new DefaultObjectSerializer(), new DefaultObjectSerializer());
  }

  /**
   * Registers an actor with the runtime.
   *
   * @param clazz            The type of actor.
   * @param objectSerializer Serializer for Actor's request and response objects.
   * @param stateSerializer  Serializer for Actor's state objects.
   * @param               Actor class type.
   */
  public  void registerActor(
        Class clazz, DaprObjectSerializer objectSerializer, DaprObjectSerializer stateSerializer) {
    registerActor(clazz,  new DefaultActorFactory(), objectSerializer, stateSerializer);
  }

  /**
   * Registers an actor with the runtime.
   *
   * @param clazz            The type of actor.
   * @param actorFactory     An optional factory to create actors. This can be used for dependency injection.
   * @param objectSerializer Serializer for Actor's request and response objects.
   * @param stateSerializer  Serializer for Actor's state objects.
   * @param               Actor class type.
   */
  public  void registerActor(
        Class clazz, ActorFactory actorFactory,
        DaprObjectSerializer objectSerializer,
        DaprObjectSerializer stateSerializer) {
    if (clazz == null) {
      throw new IllegalArgumentException("Class is required.");
    }
    if (actorFactory == null) {
      throw new IllegalArgumentException("Actor factory is required.");
    }
    if (objectSerializer == null) {
      throw new IllegalArgumentException("Object serializer is required.");
    }
    if (stateSerializer == null) {
      throw new IllegalArgumentException("State serializer is required.");
    }

    ActorTypeInformation actorTypeInfo = ActorTypeInformation.create(clazz);

    // Create ActorManager, if not yet registered.
    this.actorManagers.computeIfAbsent(actorTypeInfo.getName(), (k) -> {
      ActorRuntimeContext context = new ActorRuntimeContext<>(
              this,
              objectSerializer,
              actorFactory,
              actorTypeInfo,
              this.daprClient,
              new DaprStateAsyncProvider(this.daprClient, stateSerializer));
      this.config.addRegisteredActorType(actorTypeInfo.getName());
      return new ActorManager(context);
    });
  }

  /**
   * Deactivates an actor for an actor type with given actor id.
   *
   * @param actorTypeName Actor type name to deactivate the actor for.
   * @param actorId       Actor id for the actor to be deactivated.
   * @return Async void task.
   */
  public Mono deactivate(String actorTypeName, String actorId) {
    return Mono.fromSupplier(() -> this.getActorManager(actorTypeName))
          .flatMap(m -> m.deactivateActor(new ActorId(actorId)));
  }

  /**
   * Invokes the specified method for the actor, this is mainly used for cross
   * language invocation.
   *
   * @param actorTypeName   Actor type name to invoke the method for.
   * @param actorId         Actor id for the actor for which method will be invoked.
   * @param actorMethodName Method name on actor type which will be invoked.
   * @param payload         RAW payload for the actor method.
   * @return Response for the actor method.
   */
  public Mono invoke(String actorTypeName, String actorId, String actorMethodName, byte[] payload) {
    ActorId id = new ActorId(actorId);
    return Mono.fromSupplier(() -> this.getActorManager(actorTypeName))
          .flatMap(m -> m.activateActor(id).thenReturn(m))
          .flatMap(m -> ((ActorManager)m).invokeMethod(id, actorMethodName, payload));
  }

  /**
   * Fires a reminder for the Actor.
   *
   * @param actorTypeName Actor type name to invoke the method for.
   * @param actorId       Actor id for the actor for which method will be invoked.
   * @param reminderName  The name of reminder provided during registration.
   * @param params        Params for the reminder.
   * @return Async void task.
   */
  public Mono invokeReminder(String actorTypeName, String actorId, String reminderName, byte[] params) {
    ActorId id = new ActorId(actorId);
    return Mono.fromSupplier(() -> this.getActorManager(actorTypeName))
          .flatMap(m -> m.activateActor(id).thenReturn(m))
          .flatMap(m -> ((ActorManager)m).invokeReminder(new ActorId(actorId), reminderName, params));
  }

  /**
   * Fires a timer for the Actor.
   *
   * @param actorTypeName Actor type name to invoke the method for.
   * @param actorId       Actor id for the actor for which method will be invoked.
   * @param timerName     The name of timer provided during registration.
   * @param params        Params to trigger timer.
   * @return Async void task.
   */
  public Mono invokeTimer(String actorTypeName, String actorId, String timerName, byte[] params) {
    ActorId id = new ActorId(actorId);
    return Mono.fromSupplier(() -> this.getActorManager(actorTypeName))
          .flatMap(m -> m.activateActor(id).thenReturn(m))
          .flatMap(m -> ((ActorManager)m).invokeTimer(new ActorId(actorId), timerName, params));
  }

  /**
   * Finds the actor manager or errors out.
   *
   * @param actorTypeName Actor type for the actor manager to be found.
   * @return Actor manager instance, never null.
   * @throws IllegalStateException if cannot find actor's manager.
   */
  private ActorManager getActorManager(String actorTypeName) {
    ActorManager actorManager = this.actorManagers.get(actorTypeName);

    if (actorManager == null) {
      String errorMsg = String.format("Actor type %s is not registered with Actor runtime.", actorTypeName);
      ACTOR_TRACE.writeError(TRACE_TYPE, actorTypeName, "Actor type is not registered with runtime.");
      throw new IllegalArgumentException(errorMsg);
    }

    return actorManager;
  }

  /**
   * Build an instance of the Client based on the provided setup.
   *
   * @param channel GRPC managed channel (or null, if not using GRPC).
   * @return an instance of the setup Client
   * @throws java.lang.IllegalStateException if any required field is missing
   */
  private static DaprClient buildDaprClient(ManagedChannel channel) {
    if (Properties.API_PROTOCOL.get() == DaprApiProtocol.GRPC) {
      return new DaprGrpcClient(channel);
    }

    return new DaprHttpClient(new DaprHttpBuilder().build());
  }

  /**
   * Creates a GRPC managed channel (or null, if not applicable).
   *
   * @return GRPC managed channel or null.
   */
  private static ManagedChannel buildManagedChannel() {
    if (Properties.API_PROTOCOL.get() != DaprApiProtocol.GRPC) {
      return null;
    }

    int port = Properties.GRPC_PORT.get();
    if (port <= 0) {
      throw new IllegalStateException("Invalid port.");
    }

    return ManagedChannelBuilder.forAddress(Properties.SIDECAR_IP.get(), port)
      .usePlaintext()
      .userAgent(Version.getSdkVersion())
      .build();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void close() {
    if (channel != null && !channel.isShutdown()) {
      channel.shutdown();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy