issue with jacksonserializer and @Metadata custom objects in eventhandler

Hi,

I am trying to switch the serializer in my application from xstream to jackson. There were many issues that i managed to resolve, but this one i have not managed: basically when you have an eventhandler like this:

`
@EventHandler
public void on(CommentSet event, @MetaData(value = Security.METADATA_PRINCIPAL) User user) { // user is a pojo

`

The user pojo is serialized as a json map over the distributed command bus, and when it is deserialized on the other node the Axon invocation handler fails with an IllegalArgumentException saying that the arguments don’t match. So somehow i need to trick either Axon or Jackson into ser/deser this pojo properly. Any ideas would be appreciated ?

Thanks,
Jorg

Jorg,

Does User POJO have a no-arg constructor? If it has args make sure you annotate the constructor with @JsonCreator and also add @JsonProperty to each arg.

Also make sure you don't have fields with 2 distinct getters. If so, add @JsonIgnore to one of them.

Both are common pitfalls I see when using Jackson.

Regards,
Benoit

Hi Benoit,

Thanks, yes adding the no-arg to User was one of the fixes I had to make. I managed to get Jackson serialization to work eventually, but it’s really not trivial and i spent far more time than i hoped on it :expressionless: . In summary here is what was needed, perhaps the Axon team sees a way to improve this for everyone out-of-the-box. As things started breaking after putting the default JacksonSerializer, I realized that the configured serializer is used for

  1. event serialization into DOMAINEVENTENTRY

  2. snapshot serialization (basically your AR classes) into SNAPSHOTEVENTENTRY

  3. command serialization because of the distributed command bus

To solve 1 and 2 I had to ensure no-arg constructors on all events and make sure getters/setters were properly present. That did not solve the issue with @MetaData serialization however, i am guessing it’s because i have a complex object User in it and it might work for others without any changes. So to get this to work:

  • copy the JacksonSerializer from Axon sources locally, and remove the MetaDataDeserializer it adds to the mapper
  • provide my own serializer with adapted mapper config

`

ObjectMapper mapper = new ObjectMapper();
// needed for complex objects in MetaData
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
// needed for AR serialization
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
// needed for AR deserialization

mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// needed otherwise exceptions are not serialized over the distributed command bus
mapper.addMixIn(AxonException.class, ThrowableMixin.class);
// needed because MetaData is not so jackson friendly with its immutability
mapper.addMixIn(MetaData.class, MetaDataMixin.class);
return new CustomJacksonSerializer(mapper);

`

The mixins:

`

public class MetaDataMixin extends MetaData {

@JsonCreator
public MetaDataMixin(Map<String, ?> items) {
super(items);
}
}

`

`

public abstract class ThrowableMixin extends Throwable {

@JsonCreator
ThrowableMixin(@JsonProperty(“message”) String message, @JsonProperty(“cause”) Throwable cause) {
super(message, cause);
}
}

`

Lastly, to solve 3 I had to reenable the xstream serializer on the JGroupsConnectorFactoryBean only, because our commands are protobufs and Jackson completely chokes on them.

The net effect of all this is that we’re able to import huge batches of historical data at near constant speed, as opposed to before (see https://groups.google.com/forum/#!topic/axonframework/8mOnQFAHRls)

HTH
Jorg