XStream and StringAggregateIdentifier

Hi, I've got a problem with deserializing my events (the id it looks
like) from the filesystem event store.

This is the event in the store:

0 0 2011-08-11T17:19:53.796+02:00 579 <TournamentCreatedEvent
eventRevision="0">
<metaData>
  <values>
   <entry>
    <string>_timestamp</string>
    <dateTime>2011-08-11T17:19:53.796+02:00</dateTime>
   </entry>
   <entry>
    <string>_identifier</string>
    <uuid>58ea6d23-3325-4f5a-bbf6-22d930ac4a07</uuid>
   </entry>
  </values>
</metaData>
<sequenceNumber>0</sequenceNumber>
<aggregateIdentifier>63cac129-56e3-4f16-a34e-576e9ebf96a6</

<tournamentId reference="../aggregateIdentifier"/>
<name>Test </name>
</TournamentCreatedEvent>

The class TournamentId looks like this:

/**
* A unique identifier for tournament objects
*/
public class TournamentId extends StringAggregateIdentifier {
    private static final long serialVersionUID = 7681791369325086749L;

    public TournamentId(final String identifier) {
        super(identifier);
    }
}

A TournamentCreatedEvent looks like this:

public class TournamentCreatedEvent extends TournamentEvent {
    private static final long serialVersionUID =
-6820793106538014330L;

    private final String name;

    public TournamentCreatedEvent(
            final TournamentId tournamentId,
            final String name) {
        this.tournamentId = tournamentId;
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

I can create a new tournament, but when I try to get it out of the
repository I get this:

[java] INFO: LoggerImplFactory set to
com.google.code.morphia.logging.jdk.JDKLoggerFactory
     [java] 1 [main] INFO
org.axonframework.repository.LockingRepository - Exception occurred
while trying to load an aggregate. Releasing lock.
     [java] com.thoughtworks.xstream.converters.ConversionException:
Cannot convert type org.axonframework.domain.StringAggregateIdentifier
to type biz.wiklander.sportexchange.common.domain.TournamentId
     [java] ---- Debugging information ----
     [java] class :
biz.wiklander.sportexchange.command.server.event.TournamentCreatedEvent
     [java] required-type :
biz.wiklander.sportexchange.command.server.event.TournamentCreatedEvent
     [java] path : /
biz.wiklander.sportexchange.command.server.event.TournamentCreatedEvent/
tournamentId
     [java] line number : 1
     [java] -------------------------------
     [java] at
com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.doUnmarshal(AbstractReflectionConverter.java:
229)
     [java] at
com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.unmarshal(AbstractReflectionConverter.java:
162)
     [java] at
com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:
82)
     [java] at
com.thoughtworks.xstream.core.AbstractReferenceUnmarshaller.convert(AbstractReferenceUnmarshaller.java:
63)
     [java] at
com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:
76)
     [java] at
com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:
60)
     [java] at
com.thoughtworks.xstream.core.TreeUnmarshaller.start(TreeUnmarshaller.java:
137)
     [java] at
com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.unmarshal(AbstractTreeMarshallingStrategy.java:
33)
     [java] at
com.thoughtworks.xstream.XStream.unmarshal(XStream.java:923)
     [java] at
com.thoughtworks.xstream.XStream.unmarshal(XStream.java:909)
     [java] at com.thoughtworks.xstream.XStream.fromXML(XStream.java:
853)
     [java] at
org.axonframework.serializer.GenericXStreamSerializer.deserialize(GenericXStreamSerializer.java:
145)
     [java] at
org.axonframework.eventstore.XStreamEventSerializer.deserialize(XStreamEventSerializer.java:
129)
     [java] at
org.axonframework.eventstore.fs.EventEntry.deserialize(EventEntry.java:
56)
     [java] at org.axonframework.eventstore.fs.FileSystemEventStore
$BufferedReaderDomainEventStream.doReadNext(FileSystemEventStore.java:
297)
     [java] at org.axonframework.eventstore.fs.FileSystemEventStore
$BufferedReaderDomainEventStream.<init>(FileSystemEventStore.java:264)
     [java] at
org.axonframework.eventstore.fs.FileSystemEventStore.readEvents(FileSystemEventStore.java:
192)
     [java] at
org.axonframework.eventstore.fs.FileSystemEventStore.readEvents(FileSystemEventStore.java:
124)
     [java] at
org.axonframework.eventsourcing.EventSourcingRepository.doLoad(EventSourcingRepository.java:
121)
     [java] at
org.axonframework.eventsourcing.EventSourcingRepository.doLoad(EventSourcingRepository.java:
54)
     [java] at
org.axonframework.repository.AbstractRepository.load(AbstractRepository.java:
70)
     [java] at
org.axonframework.repository.LockingRepository.load(LockingRepository.java:
119)
     [java] at
org.axonframework.repository.AbstractRepository.load(AbstractRepository.java:
80)
     [java] at
biz.wiklander.sportexchange.command.server.base.TournamentHandler.getTournament(TournamentHandler.java:
76)
     [java] at
biz.wiklander.sportexchange.command.server.base.TournamentHandler.handle(TournamentHandler.java:
67)
     [java] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native
Method)
     [java] at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
39)
     [java] at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
25)
     [java] at java.lang.reflect.Method.invoke(Method.java:597)
     [java] at org.axonframework.util.Handler.invoke(Handler.java:
110)
     [java] at
org.axonframework.util.AbstractHandlerInvoker.invokeHandlerMethod(AbstractHandlerInvoker.java:
77)
     [java] at
org.axonframework.commandhandling.annotation.AnnotationCommandHandlerAdapter.handle(AnnotationCommandHandlerAdapter.java:
76)
     [java] at
org.axonframework.commandhandling.DefaultInterceptorChain.proceed(DefaultInterceptorChain.java:
62)
     [java] at
org.axonframework.commandhandling.DefaultInterceptorChain.proceed(DefaultInterceptorChain.java:
68)
     [java] at
org.axonframework.commandhandling.SimpleCommandBus.doDispatch(SimpleCommandBus.java:
115)
     [java] at
org.axonframework.commandhandling.SimpleCommandBus.dispatch(SimpleCommandBus.java:
92)
     [java] at
biz.wiklander.sportexchange.command.server.LocalCommandBus.dispatch(LocalCommandBus.java:
22)

Hi Per,

looks like a bug caused by a rather naive assumption on my side. It has to do with the AggregateIdentifierConverter assuming that it can always assign a StringAggregateIdentifier (the instance type that deserializes the fastest) to any field that is assignable to AggregateIdentifier.

Well, long story short: I’ll have to fix this one. :wink:

Thanks.

Allard

Thanks for the quick reply! Is it a quick fix? It is stopping my work
for now.

Hi Per,

the quick fix would be to declare the field as a “StringAggregateIdentifier” or “AggregateIdentifier” instead. You getter can still return a TournamentId, but you’d have to convert it inside the getter.
A small optimization could be to create a static constructor method on TournamentId:

public static TournamentId fromAggregateIdentifier(AggregateIdentifier aggregateIdentifier) {
if (aggregateIdentifier instanceof TournamentId) {
return (TournamentId) aggregateIdentifier;
}
return new TournamentId(aggregateIdentifier.asString());
}

In the meantime, I’ll try to make some time to fix it “for real”.

Cheers,

Allard

Hi Per,

I managed to reproduce the problem, and noticed something in your code.

You are attaching the aggregate identifier to a domain event. Axon already stores that for you and is accessible through getAggregateIdentifier(). That with the static constructor shown below should do the trick for now.
Meanwhile I’m improving the support for custom aggregate identifiers in the XStreamEventSerializer. Looks like it is a backwards compatible solution, so it’s in Axon soon.

Cheers,

Allard

Hi Per,

the problem should be solved with the new 1.2-SNAPSHOT I just deployed. Let me know whether it works for you.

Cheers,

Allard

Scenarios run: 1, Failures: 0, Pending: 0, Time elapsed: 2.663 sec

1 total behavior ran with no failures

Yup, looking good from here :slight_smile: Thanks!