baker icon indicating copy to clipboard operation
baker copied to clipboard

Limitations of the InteractionInstances: Lambda interface implementation with higher kinded types

Open VledicFranco opened this issue 5 years ago • 3 comments

Implementing an interaction by creating an interface, instantiating the interface with a lambda, and then using the reflection API to create an InteractionInstance blows into a runtime exception WHEN the ingredients have a higher kinded type like List[A]. This is because the lambda instantiation of the interface erases the parametrized type. See example below:

trait ReserveItems {

  def apply(orderId: String, items: List[String]): Future[WebshopRecipeReflection.ReserveItemsOutput]
}

  val reserveItemsImplementation: ReserveItems =
    (orderId: String, items: List[String]) => {

      // Http call to the Warehouse service
      val response: Future[Either[List[String], List[String]]] =
      // This is mocked for the sake of the example
        Future.successful(Right(items))

      // Build an event instance that Baker understands
      response.map {
        case Left(unavailableItems) =>
          WebshopRecipeReflection.OrderHadUnavailableItems(unavailableItems)
        case Right(reservedItems) =>
          WebshopRecipeReflection.ItemsReserved(reservedItems)
      }
    }

  val alternativeReserveItemInstance: InteractionInstance =
    InteractionInstance.unsafeFrom(reserveItemsImplementation)
Caused by: java.lang.IllegalArgumentException: Unsupported parameter type for interaction implementation 'ReserveItems'
	at com.ing.baker.runtime.scaladsl.InteractionInstance$.$anonfun$unsafeFrom$7(InteractionInstance.scala:89)
	at scala.collection.TraversableLike.$anonfun$map$1(TraversableLike.scala:234)
	at scala.collection.IndexedSeqOptimized.foreach(IndexedSeqOptimized.scala:32)
	at scala.collection.IndexedSeqOptimized.foreach$(IndexedSeqOptimized.scala:29)
	at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:191)
	at scala.collection.TraversableLike.map(TraversableLike.scala:234)
	at scala.collection.TraversableLike.map$(TraversableLike.scala:227)
	at scala.collection.mutable.ArrayOps$ofRef.map(ArrayOps.scala:191)
	at com.ing.baker.runtime.scaladsl.InteractionInstance$.unsafeFrom(InteractionInstance.scala:86)
	at webshop.WebshopInstancesReflection$.<init>(WebshopInstancesReflection.scala:54)
	at webshop.WebshopInstancesReflection$.<clinit>(WebshopInstancesReflection.scala)
	... 11 more
Caused by: java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
	at com.ing.baker.types.package$.getTypeParameter(package.scala:25)
	at com.ing.baker.types.modules.ScalaModules$ListModule.readType(ScalaModules.scala:13)
	at com.ing.baker.types.modules.ScalaModules$ListModule.readType(ScalaModules.scala:10)
	at com.ing.baker.types.TypeAdapter.readType(TypeAdapter.scala:48)
	at com.ing.baker.types.Converters$.readJavaType(Converters.scala:47)
	at com.ing.baker.runtime.scaladsl.InteractionInstance$.$anonfun$unsafeFrom$7(InteractionInstance.scala:87)
	... 21 more

VledicFranco avatar Jul 16 '19 09:07 VledicFranco

It was found that it is not lambda specific, this will also break it:

val reserveItemsInstance: InteractionInstance = InteractionInstance.unsafeFrom(
    new ReserveItems {

      def apply(orderId: String, items: List[String]): Future[WebshopRecipeReflection.ReserveItemsOutput] = {

        // Http call to the Warehouse service
        val response: Future[Either[List[String], List[String]]] =
        // This is mocked for the sake of the example
          Future.successful(Right(items))

        // Build an event instance that Baker understands
        response.map {
          case Left(unavailableItems) =>
            WebshopRecipeReflection.OrderHadUnavailableItems(unavailableItems)
          case Right(reservedItems) =>
            WebshopRecipeReflection.ItemsReserved(reservedItems)
        }
      }
    }
  )

VledicFranco avatar Jul 16 '19 09:07 VledicFranco

Also in Java interactions must be direct class instances, if they are a reference to other class members or to static attributes, the Interactionmanager can't access them

VledicFranco avatar Jul 17 '19 09:07 VledicFranco

Not sure if my issue is rooted in same cause but sounds close so posting here first.

Have interactions that use SessionId as ingredient.

case class SessionId(value: java.util.UUID) extends AnyVal

At runtime am getting "No implementation provided for interaction". Tried to trace in debugger and seems the issue is the mismatch between these

RecordType(WrappedArray(RecordField(mostSigBits,Int64), RecordField(leastSigBits,Int64)))

RecordType(WrappedArray(RecordField(value,RecordType(WrappedArray(RecordField(mostSigBits,Int64), RecordField(leastSigBits,Int64))))))

Sounds like the UUID value gets unwrapped. :thinking: Does this make any sense to you? Is my design of interaction valid (taking case classes as ingredients?

I do use complex case classes as ingredients and they do not cause this issue.


UPDATE: Solved. This was caused by SessionId being an AnyVal.

SemanticBeeng avatar Aug 29 '19 08:08 SemanticBeeng