Upcasting for class renaming: Old class needs to be available?

Hi,
I would like to rename an event class to something different. I could do this by modifying the event store (JDBC), but I would not like to tamper with this.

The old class name ist OrderRegistrationEvent, the new one ContractRegistrationEvent.

Instead I tried to use an upcaster, like this:

@Component
class EventUpcaster : SingleEventUpcaster() {

  companion object {
    private val OLD_TYPE = SimpleSerializedType(
      "com.test.pandia.domain.buildingobject.event.OrderRegisteredEvent",
      null
    )
    private val NEW_TYPE = SimpleSerializedType(
      "com.test.pandia.domain.buildingobject.event.ContractRegisteredEvent",
      null
    )
  }

  override fun canUpcast(intermediateRepresentation: IntermediateEventRepresentation): Boolean {
    return OLD_TYPE == intermediateRepresentation.type
  }

  override fun doUpcast(intermediateRepresentation: IntermediateEventRepresentation): IntermediateEventRepresentation {
    return intermediateRepresentation.upcastPayload(
      NEW_TYPE,
      Document::class.java
    ) { document -> document }
  }
}

However, there are several questions:

  1. I only had the axon-spring-boot-starter dependency declared, but all examples require as second parameter for upcastPayload() a org.dom4j.Document, which was not available until I added dom4j as a dependency. How did serializing / deserializing work without this dependency in the first place?
  2. I’ve renamed the class in the code, but it looks like that the original class is required as I get the following error message:
Caused by: com.thoughtworks.xstream.mapper.CannotResolveClassException: de.mnet.pandiacore.domain.buildingobject.event.OrderRegisteredEvent
	at com.thoughtworks.xstream.mapper.DefaultMapper.realClass(DefaultMapper.java:81) ~[xstream-1.4.21.jar:1.4.21]

Why do I deserialize into Document when I still need the original class? The only thing that works is to add an alias to XStream for the old class name, but why is this required?

I’ve also tried this:

@Component
class EventUpcaster : EventTypeUpcaster(
  "de.mnet.pandiacore.domain.buildingobject.event.OrderRegisteredEvent",
  null,
  "de.mnet.pandiacore.domain.buildingobject.event.ContractRegisteredEvent",
  null,
)

Same result. If something is missing for XStream-based serialization, could you please update the documentation?

Hi @Matthias_Jeschke, I think you’re hitting an issue with the tags of the xml format that’s stored of your event. When using the XStreamSerializer of Axon Framework, you get an XML format of your event (as I am sure you’re aware of). The top tag typically equals the FQCN of the object that’s serialized.

Hence, the OrderRegisteredEvent you’re upcasting will get a new payload type, both in the SingleEventUpcaster and EventTypeUpcaster. However, the payload still refers to the old OrderRegisteredEvent, I think.

If my assumption is correct, you can work around this by registering an alias with XStream, as described here.

So, please give that a try; I hope it helps!


…could you please update the documentation?

Although my answer would typically be a wholehearted yes, this time around I will not. This has to do with the fact we’re facing out support for XStream entirely. Axon Framework 5, which we released last year October, already does not have XStreamSerializer anymore. We’ve made this choice based on the heavy reflection use of XStream that is no longer desirable by default. This thread on the XStream repository itself may be insightful to comprehend the choice.

Due to this, we’re working on migration information from AF4 to AF5. For serializer, the issue for this can be found here.

However, assuming that my suggestion solves it and you feel strongly that it should be documented for Axon Framework 4, contributions can always be made. Axon Framework, and the documentation, are open source after all. If you’d go that route, a PR would be pointed towards a change on this file.


In all, I hope this helps you out, @Matthias_Jeschke!

Hi @Steven_van_Beelen,

Thank you for your response!

Yes, adding an alias works, but it’s not that intuitive in this case as (if I understand the upcaster correctly) we want to deserialize into a (XML) Document, then modify the Document and write this as the new payload, so the upcaster tries to “circumvent” deserializing into the (now maybe not existing) class. However, XStream still tries to deserialize into the old class (as it is written as the top tag) and fails (although we request to deserialize the payload into a Document).

I can understand that you will not update the documentation if the Serializer is about to change, so this discussion might help people with a similar problem until Axon 5 has been adopted by the majority.

1 Like

I get you here, @Matthias_Jeschke. It is suboptimal for sure… thanks for understanding, though :pray:

I do agree fully with you: I hope this thread will help others facing the same issue!