AxonFramework: Bug: Regression in handling MultipleInstancesResponseType in a query in Axon 4.3

Hello,

I am encountering an issue when upgrading from Axon 4.2.1 to Axon 4.3.

I have created a repo here (https://github.com/vab2048/axon-4.3-bug-reproduction) which contains code and instructions on how to reproduce the issue.

For reference here is a stack trace:

2020-04-21 19:57:22.348 ERROR 8316 --- [nio-8080-exec-7] o.a.c.c.C.[.[.[.[dispatcherServlet]      : Servlet.service() for servlet [dispatcherServlet] threw exception

java.lang.IllegalArgumentException: Retrieved response [class java.util.ArrayList] is not convertible to a List of the expected response type [class com.example.axon.bug.query.model.OrderState]
	at org.axonframework.messaging.responsetypes.MultipleInstancesResponseType.convert(MultipleInstancesResponseType.java:113) ~[axon-messaging-4.3.jar:4.3]
	at org.axonframework.messaging.responsetypes.MultipleInstancesResponseType.convert(MultipleInstancesResponseType.java:44) ~[axon-messaging-4.3.jar:4.3]
	at org.axonframework.messaging.responsetypes.ConvertingResponseMessage.getPayload(ConvertingResponseMessage.java:77) ~[axon-messaging-4.3.jar:4.3]
	at org.axonframework.queryhandling.DefaultQueryGateway.lambda$query$1(DefaultQueryGateway.java:87) ~[axon-messaging-4.3.jar:4.3]
	at java.util.concurrent.CompletableFuture$UniAccept.tryFire(CompletableFuture.java:714) ~[?:?]
	at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506) ~[?:?]
	at java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2137) ~[?:?]
	at org.axonframework.axonserver.connector.query.AxonServerQueryBus$1.onNext(AxonServerQueryBus.java:343) ~[axon-server-connector-4.3.jar:4.3]
	at org.axonframework.axonserver.connector.query.AxonServerQueryBus$1.onNext(AxonServerQueryBus.java:337) ~[axon-server-connector-4.3.jar:4.3]
	at io.grpc.stub.ClientCalls$StreamObserverToCallListenerAdapter.onMessage(ClientCalls.java:429) ~[grpc-stub-1.22.1.jar:1.22.1]
	at io.grpc.ForwardingClientCallListener.onMessage(ForwardingClientCallListener.java:33) ~[grpc-api-1.22.1.jar:1.22.1]
	at io.grpc.ForwardingClientCallListener.onMessage(ForwardingClientCallListener.java:33) ~[grpc-api-1.22.1.jar:1.22.1]
	at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1MessagesAvailable.runInternal(ClientCallImpl.java:596) ~[grpc-core-1.22.1.jar:1.22.1]
	at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1MessagesAvailable.runInContext(ClientCallImpl.java:581) ~[grpc-core-1.22.1.jar:1.22.1]
	at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37) ~[grpc-core-1.22.1.jar:1.22.1]
	at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123) ~[grpc-core-1.22.1.jar:1.22.1]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630) ~[?:?]
	at java.lang.Thread.run(Thread.java:832) [?:?]

You can find instructions on how to reproduce in the README of the linked github.

Regards, vab2048.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 16 (8 by maintainers)

Commits related to this issue

Most upvoted comments

I will just add a minor comment to highlight the alternative to customising the seriailzer (I prefer this alternative):

  • Create a wrapper class for the items e.g. public record ToDos(List<ToDo> todos) {}
  • Then use ResponseTypes.instanceOf(ToDos.class) in the query handler (rather than using ResponseTypes.multipleInstancesOf(ToDo.class)

Records make having the wrapper class easy - and Jackson is able to serialize/deserialize without any issues (as long as you are on one of the latest versions). This avoids the patchwork of having to allow for lenientDeserialization.

Indeed the following bean definition also solved the problem:

	@Qualifier("messageSerializer")
	@Bean
	public Serializer messageSerializer(ObjectMapper mapper) {
		return JacksonSerializer.builder()
				.objectMapper(mapper)
				.lenientDeserialization()
				.defaultTyping()
				.build();
	}

Thanks