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

kalix.javasdk.impl.action.ReflectiveActionRouter.scala Maven / Gradle / Ivy

/*
 * Copyright 2021 Lightbend Inc.
 *
 * 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 kalix.javasdk.impl.action

import akka.NotUsed
import akka.stream.javadsl.Source
import com.google.protobuf.any.{ Any => ScalaPbAny }
import kalix.javasdk.action.Action
import kalix.javasdk.action.MessageEnvelope
import kalix.javasdk.impl.AnySupport.ProtobufEmptyTypeUrl
import kalix.javasdk.impl.CommandHandler
import kalix.javasdk.impl.InvocationContext
// TODO: abstract away reactor dependency
import reactor.core.publisher.Flux

class ReflectiveActionRouter[A <: Action](
    action: A,
    commandHandlers: Map[String, CommandHandler],
    ignoreUnknown: Boolean)
    extends ActionRouter[A](action) {

  private def commandHandlerLookup(commandName: String) =
    commandHandlers.getOrElse(commandName, throw new RuntimeException(s"no matching method for '$commandName'"))

  override def handleUnary(commandName: String, message: MessageEnvelope[Any]): Action.Effect[_] = {

    val commandHandler = commandHandlerLookup(commandName)

    val invocationContext =
      InvocationContext(
        message.payload().asInstanceOf[ScalaPbAny],
        commandHandler.requestMessageDescriptor,
        message.metadata())

    val inputTypeUrl = message.payload().asInstanceOf[ScalaPbAny].typeUrl
    val methodInvoker = commandHandler.lookupInvoker(inputTypeUrl)

    methodInvoker match {
      case Some(invoker) =>
        inputTypeUrl match {
          case ProtobufEmptyTypeUrl =>
            invoker
              .invoke(action)
              .asInstanceOf[Action.Effect[_]]
          case _ =>
            invoker
              .invoke(action, invocationContext)
              .asInstanceOf[Action.Effect[_]]
        }
      case None if ignoreUnknown => ActionEffectImpl.Builder.ignore()
      case None =>
        throw new NoSuchElementException(
          s"Couldn't find any method with input type [$inputTypeUrl] in Action [$action].")
    }
  }

  override def handleStreamedOut(
      commandName: String,
      message: MessageEnvelope[Any]): Source[Action.Effect[_], NotUsed] = {

    val componentMethod = commandHandlerLookup(commandName)

    val context =
      InvocationContext(
        message.payload().asInstanceOf[ScalaPbAny],
        componentMethod.requestMessageDescriptor,
        message.metadata())

    val inputTypeUrl = message.payload().asInstanceOf[ScalaPbAny].typeUrl

    componentMethod.lookupInvoker(inputTypeUrl) match {
      case Some(methodInvoker) =>
        val response = methodInvoker.invoke(action, context).asInstanceOf[Flux[Action.Effect[_]]]
        Source.fromPublisher(response)
      case None if ignoreUnknown => Source.empty()
      case None =>
        throw new NoSuchElementException(
          s"Couldn't find any method with input type [$inputTypeUrl] in Action [$action].")
    }
  }

  override def handleStreamedIn(commandName: String, stream: Source[MessageEnvelope[Any], NotUsed]): Action.Effect[_] =
    throw new IllegalArgumentException("Stream in calls are not supported")

  // TODO: to implement
  override def handleStreamed(
      commandName: String,
      stream: Source[MessageEnvelope[Any], NotUsed]): Source[Action.Effect[_], NotUsed] =
    throw new IllegalArgumentException("Stream in calls are not supported")
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy