Serialization Issue with JDK 17 and Spring Boot 3

We have encountered an issue while upgrading our application, which is built with Spring Boot and Axon Framework, from Java 11 and Spring Boot 2.7 to Java 17 and Spring Boot 3. We’re using Axon v4.9.1. We use PostgreSQL as an event store. The problem revolves around a regression in XStream serialization, leading to the following error:

2024-02-05T10:40:47.708Z ERROR 1 --- [integration-service] [nio-8080-exec-2] o.a.c.c.C.[.[.[.[dispatcherServlet]      : Servlet.service() for servlet [dispatcherServlet] in context with path [/integration] threw exception [Request processing failed: org.axonframework.eventsourcing.eventstore.EventStoreException: Cannot reuse aggregate identifier [c8085e30-00d7-416d-a466-37727dd7c9e6] to create aggregate [JobAggregate] since identifiers need to be unique.] with root cause
2024-02-05T10:40:47.712386774Z 
2024-02-05T10:40:47.712442504Z com.thoughtworks.xstream.converters.ConversionException: No converter available
2024-02-05T10:40:47.712454621Z ---- Debugging information ----
2024-02-05T10:40:47.712463905Z message             : No converter available
2024-02-05T10:40:47.712476905Z type                : java.util.Collections$EmptyList
2024-02-05T10:40:47.712486447Z converter           : com.thoughtworks.xstream.converters.reflection.ReflectionConverter
2024-02-05T10:40:47.712496123Z message[1]          : Unable to make field protected transient int java.util.AbstractList.modCount accessible: module java.base does not "opens java.util" to unnamed module @11b03c1f
2024-02-05T10:40:47.712505918Z -------------------------------
2024-02-05T10:40:47.712514264Z 	at com.thoughtworks.xstream.core.DefaultConverterLookup.lookupConverterForType(DefaultConverterLookup.java:88) ~[xstream-1.4.20.jar:1.4.20]
2024-02-05T10:40:47.712525184Z 	at com.thoughtworks.xstream.XStream$1.lookupConverterForType(XStream.java:478) ~[xstream-1.4.20.jar:1.4.20]
2024-02-05T10:40:47.712527769Z 	at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:49) ~[xstream-1.4.20.jar:1.4.20]
2024-02-05T10:40:47.712530528Z 	at com.thoughtworks.xstream.core.AbstractReferenceMarshaller$1.convertAnother(AbstractReferenceMarshaller.java:83) ~[xstream-1.4.20.jar:1.4.20]
2024-02-05T10:40:47.712537666Z 	at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.marshallField(AbstractReflectionConverter.java:270) ~[xstream-1.4.20.jar:1.4.20]
2024-02-05T10:40:47.712540436Z 	at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter$2.writeField(AbstractReflectionConverter.java:174) ~[xstream-1.4.20.jar:1.4.20]
2024-02-05T10:40:47.712543070Z 	at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.doMarshal(AbstractReflectionConverter.java:262) ~[xstream-1.4.20.jar:1.4.20]
2024-02-05T10:40:47.712546915Z 	at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.marshal(AbstractReflectionConverter.java:90) ~[xstream-1.4.20.jar:1.4.20]
2024-02-05T10:40:47.712549750Z 	at com.thoughtworks.xstream.core.AbstractReferenceMarshaller.convert(AbstractReferenceMarshaller.java:68) ~[xstream-1.4.20.jar:1.4.20]
2024-02-05T10:40:47.712552357Z 	at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:59) ~[xstream-1.4.20.jar:1.4.20]
2024-02-05T10:40:47.712554926Z 	at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:44) ~[xstream-1.4.20.jar:1.4.20]
2024-02-05T10:40:47.712557543Z 	at com.thoughtworks.xstream.core.TreeMarshaller.start(TreeMarshaller.java:83) ~[xstream-1.4.20.jar:1.4.20]
2024-02-05T10:40:47.712560067Z 	at com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.marshal(AbstractTreeMarshallingStrategy.java:37) ~[xstream-1.4.20.jar:1.4.20]
2024-02-05T10:40:47.712562690Z 	at com.thoughtworks.xstream.XStream.marshal(XStream.java:1303) ~[xstream-1.4.20.jar:1.4.20]
2024-02-05T10:40:47.712570230Z 	at com.thoughtworks.xstream.XStream.marshal(XStream.java:1292) ~[xstream-1.4.20.jar:1.4.20]
2024-02-05T10:40:47.712574376Z 	at com.thoughtworks.xstream.XStream.toXML(XStream.java:1265) ~[xstream-1.4.20.jar:1.4.20]
2024-02-05T10:40:47.712578234Z 	at org.axonframework.serialization.xml.XStreamSerializer.doSerialize(XStreamSerializer.java:132) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712582040Z 	at org.axonframework.serialization.AbstractXStreamSerializer.serialize(AbstractXStreamSerializer.java:110) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712585954Z 	at org.axonframework.serialization.SerializedObjectHolder.serializePayload(SerializedObjectHolder.java:57) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712590101Z 	at org.axonframework.messaging.GenericMessage.serializePayload(GenericMessage.java:167) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712597110Z 	at org.axonframework.messaging.MessageDecorator.serializePayload(MessageDecorator.java:66) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712601455Z 	at org.axonframework.eventhandling.AbstractEventEntry.<init>(AbstractEventEntry.java:84) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712604739Z 	at org.axonframework.eventhandling.AbstractDomainEventEntry.<init>(AbstractDomainEventEntry.java:55) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712607868Z 	at org.axonframework.eventhandling.AbstractSequencedDomainEventEntry.<init>(AbstractSequencedDomainEventEntry.java:54) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712610721Z 	at org.axonframework.eventsourcing.eventstore.jpa.DomainEventEntry.<init>(DomainEventEntry.java:49) ~[axon-eventsourcing-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712613417Z 	at org.axonframework.eventsourcing.eventstore.jpa.JpaEventStorageEngine.createEventEntity(JpaEventStorageEngine.java:427) ~[axon-eventsourcing-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712616035Z 	at org.axonframework.eventsourcing.eventstore.jpa.JpaEventStorageEngine.lambda$null$5(JpaEventStorageEngine.java:320) ~[axon-eventsourcing-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712618766Z 	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) ~[na:na]
2024-02-05T10:40:47.712621338Z 	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625) ~[na:na]
2024-02-05T10:40:47.712623866Z 	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) ~[na:na]
2024-02-05T10:40:47.712626394Z 	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na]
2024-02-05T10:40:47.712628917Z 	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) ~[na:na]
2024-02-05T10:40:47.712631445Z 	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) ~[na:na]
2024-02-05T10:40:47.712634005Z 	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
2024-02-05T10:40:47.712636536Z 	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) ~[na:na]
2024-02-05T10:40:47.712641355Z 	at org.axonframework.eventsourcing.eventstore.jpa.JpaEventStorageEngine.lambda$appendEvents$6(JpaEventStorageEngine.java:320) ~[axon-eventsourcing-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712644084Z 	at org.axonframework.common.transaction.TransactionManager.executeInTransaction(TransactionManager.java:47) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712646715Z 	at org.axonframework.eventsourcing.eventstore.jpa.JpaEventStorageEngine.appendEvents(JpaEventStorageEngine.java:318) ~[axon-eventsourcing-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712649367Z 	at org.axonframework.eventsourcing.eventstore.AbstractEventStorageEngine.appendEvents(AbstractEventStorageEngine.java:105) ~[axon-eventsourcing-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712652004Z 	at org.axonframework.eventsourcing.eventstore.AbstractEventStore.prepareCommit(AbstractEventStore.java:66) ~[axon-eventsourcing-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712654891Z 	at org.axonframework.eventhandling.AbstractEventBus.doWithEvents(AbstractEventBus.java:256) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712657562Z 	at org.axonframework.eventhandling.AbstractEventBus.lambda$null$11(AbstractEventBus.java:170) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712660135Z 	at org.axonframework.messaging.unitofwork.MessageProcessingContext.notifyHandlers(MessageProcessingContext.java:72) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712664025Z 	at org.axonframework.messaging.unitofwork.DefaultUnitOfWork.notifyHandlers(DefaultUnitOfWork.java:109) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712668063Z 	at org.axonframework.messaging.unitofwork.AbstractUnitOfWork.changePhase(AbstractUnitOfWork.java:236) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712672023Z 	at org.axonframework.messaging.unitofwork.AbstractUnitOfWork.commitAsRoot(AbstractUnitOfWork.java:87) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712676036Z 	at org.axonframework.messaging.unitofwork.AbstractUnitOfWork.commit(AbstractUnitOfWork.java:75) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712679886Z 	at org.axonframework.messaging.unitofwork.DefaultUnitOfWork.executeWithResult(DefaultUnitOfWork.java:95) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712683896Z 	at org.axonframework.commandhandling.SimpleCommandBus.lambda$handle$2(SimpleCommandBus.java:200) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712687941Z 	at org.axonframework.tracing.Span.runSupplier(Span.java:163) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712691761Z 	at org.axonframework.commandhandling.SimpleCommandBus.handle(SimpleCommandBus.java:191) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712695731Z 	at org.axonframework.commandhandling.SimpleCommandBus.doDispatch(SimpleCommandBus.java:165) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712706291Z 	at org.axonframework.commandhandling.SimpleCommandBus.lambda$dispatch$1(SimpleCommandBus.java:131) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712710192Z 	at org.axonframework.tracing.Span.run(Span.java:101) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712714285Z 	at org.axonframework.commandhandling.SimpleCommandBus.dispatch(SimpleCommandBus.java:125) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712721526Z 	at org.axonframework.commandhandling.gateway.AbstractCommandGateway.send(AbstractCommandGateway.java:76) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712725899Z 	at org.axonframework.commandhandling.gateway.DefaultCommandGateway.send(DefaultCommandGateway.java:83) ~[axon-messaging-4.9.1.jar:4.9.1]
2024-02-05T10:40:47.712729740Z 	at org.axonframework.commandhandling.gateway.DefaultCommandGateway.sendAndWait(DefaultCommandGateway.java:100) ~[axon-messaging-4.9.1.jar:4.9.1]

The error seems related to recent changes in XStream and JDK 17.

That’s an example of an event in our application

@Builder
@Data
@Event
@XStreamAlias("JobCreatedEvent")
public class JobCreatedEvent implements Serializable {
    private UUID jobId;
    private String jobName;
    @XStreamAlias("notificationInformation")
    private NotificationInformation notificationInformation;
}

Questions:

  1. Considering the serialization issue, is using XStream within Axon Framework still possible?
  2. Is it now recommended to use Jackson instead of XStream for serialization within the Axon Framework?
  3. Can we seamlessly switch to Jackson while retaining the ability to deserialize events previously serialized with XStream in the same event store?
  4. Are Sagas also serialized using XStream, and would this be affected by the mentioned issue?

Thank you!

  1. By adding a JVM flag like --add-opens java.base/java.util=ALL-UNNAMED the specific error should be fixed. So yes, you can still use XStream with Java 17. Because it’s depending a lot on reflection, and Java trimming down reflection and not working with native, you might want to move to Jackson at some time. This issue contains some information about how it still can be used and how future proof it is.
  2. Yes, if you start a new project we definitely advise to start using Jackson.
  3. You will need upcasters to turn the XML format into a JSON format. At least for all the stored events. But maybe also for snapshots and other stored data.
  4. This includes saga’s. Depending on the information in the Saga you might not have the same specific issue. But, by setting some flags opening up some classes you should be able to work around any of these kind of issues.
1 Like

Hello Gerard, thank you for your reply!

Is it possible to convert the existing events from XML to JSON format in the event store and we don’t use upcasters for our events? If so, how should we proceed? Should we create an application that reads the current events from the event store and then dumps them into the second one in JSON format or there is another way?

Yes, something like that is certainly possible. Be sure to properly test before doing something like that on prod. It’s easy to make mistakes, for example without some annotation or configuration Jackson will not populate/serialize private fields.

1 Like