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

kalix.springsdk.impl.eventsourcedentity.EventSourcedHandlersExtractor.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.springsdk.impl.eventsourcedentity

import kalix.springsdk.annotations.EventHandler
import kalix.springsdk.impl.MethodInvoker
import kalix.springsdk.impl.SpringSdkMessageCodec
import kalix.springsdk.impl.reflection.ParameterExtractors

import java.lang.reflect.{ Method, Modifier, ParameterizedType }

object EventSourcedHandlersExtractor {
  def handlersFrom(entityClass: Class[_], messageCodec: SpringSdkMessageCodec): EventSourceEntityHandlers = {

    val annotatedHandlers = entityClass.getDeclaredMethods
      .filter(_.getAnnotation(classOf[EventHandler]) != null)
      .toList

    // the type parameter from the entity defines the return type of each event handler
    val returnType =
      entityClass.getGenericSuperclass
        .asInstanceOf[ParameterizedType]
        .getActualTypeArguments
        .head
        .asInstanceOf[Class[_]]
    val (invalidHandlers, validSignatureHandlers) = annotatedHandlers.partition((m: Method) =>
      m.getParameterCount != 1 || !Modifier.isPublic(m.getModifiers) || (returnType != m.getReturnType))

    def eventTypeExtractor: Method => Class[_] = (mt: Method) => mt.getParameterTypes.head
    val eventTypeGrouped = validSignatureHandlers
      .groupBy(eventTypeExtractor)

    val (duplicatedEventTypes, validHandlers) = eventTypeGrouped.partition(_._2.length > 1)

    val errorsForSignatures =
      if (invalidHandlers.isEmpty) List.empty
      else
        List(
          HandlerValidationError(
            invalidHandlers,
            "must be public, with exactly one parameter and return type '" + returnType.getTypeName + "'"))
    val errorsForDuplicates =
      for (elem <- duplicatedEventTypes)
        yield HandlerValidationError(
          elem._2,
          "cannot have duplicate event handlers for the same event type: '" + elem._1.getName + "'")

    EventSourceEntityHandlers(
      handlers = validHandlers.map { case (classType, methods) =>
        messageCodec.typeUrlFor(classType) -> MethodInvoker(
          methods.head,
          ParameterExtractors.AnyBodyExtractor[AnyRef](classType))
      },
      errors = errorsForSignatures ++ errorsForDuplicates.toList)
  }
}

private[springsdk] final case class EventSourceEntityHandlers private (
    handlers: Map[String, MethodInvoker],
    errors: List[HandlerValidationError])

private[springsdk] final case class HandlerValidationError(methods: List[Method], description: String) {
  override def toString: String =
    s"ValidationError(reason='$description', offendingMethods=${methods.map(_.getName)}"
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy