Axon 2 -> 3 migration

Hi guys,

I just spent 2 days trying to migrate from 2.x to 3.M3. It involved refactoring about 100 command handler Spring components with at least 3 commands on each component.

The new syntax is nice and decoupled. After I thought everything was done, I started up the application only to find that the app couldn’t find any command handlers. So I wasn’t done after all. The new MessageHandler interface had to be implemented everywhere as well as eventProcessors and command handler registration. I started looking at the latest source code but M3 is out of date with the latest code as there has been a lot of Spring additions and changes lately. Lacking documentation for M3 I tried to compile my own SNAPSHOT version of what’s on master but that doesn’t compile.

With the state of the current source code not being able to compile and as I am not sure if there is a nightly snapshot lying around or what it contains, I cannot test using M3 or latest source and I cannot continue with the migration.

If you want fellow developers to help out it would be nice if latest the code compiled in lieu of no docs. Right now I am getting a AxonFramework/core/src/test/java/org/axonframework/eventhandling/async/AsynchronousEventProcessorConcurrencyTest.java:[47,73] incompatible types: bad return type in method reference
[ERROR] ? cannot be converted to java.lang.Object.

Cheers,
Bjorn

Hi Bjorn,

how did you create these “100 command handler Spring components”? If they were @CommandHandler annotated methods, there is nothing you need to change between 2 and 3. Could you elaborate on that?

Regarding the MessageHandler interface, is that the spring messaging one? Or are you referring to MessageHandlingMember? The latter is really something that you shouldn’t have to deal with, as it’s an Axon-internal class to describe an annotated method.

An then the compilation. Since the code compiles on my machine, as well as the build server (see status of master branch on https://travis-ci.org/AxonFramework/AxonFramework/). Which version of Java are you compiling with? There have been a number of Java 8 releases where javac contained a bug regarding generics. Upgrading to the latest Java 8 JDK will resolve the issue.

Currently, I am working on simplifying the setup of Axon infrastructure components using a configuration DSL. It is progressing quite well and will probably be available in a milestone release soon. That should resolve some issues with the “manual” registration of command handlers, etc.

Cheers,

Allard

Hi Allard,

Let me be as detailed as I can:

  • I’m running on a MacBook Pro with the latest version of OS X.
  • I have the JDK 8 102 installed (latest)
  • I have Maven v3.3.9 installed
  • I am developing with IntelliJ 2016.2.2
  • I did a pull now (2016-8-27) and it fails with the same error I reported yesterday. See attached Maven log for details.

Migration path:

I am using @AnnotationDriven and all my command handler methods are annotated with @CommandHandler. Currently my command handler methods are not on the aggregate but in a separate class marked @Component.

  • I removed all “extends AbstractAnnotatedAggregateRoot” from my aggregates

  • I statically imported this in my aggregates
    import static org.axonframework.commandhandling.model.AggregateLifecycle.apply;
    import static org.axonframework.commandhandling.model.AggregateLifecycle.markDeleted;

  • I updated the import statements for:

import org.axonframework.eventsourcing.annotation.AggregateIdentifier;
import org.axonframework.eventsourcing.annotation.EventSourcingHandler;
import org.axonframework.commandhandling.annotation.CommandHandler;
import org.axonframework.eventstore.mongo.MongoTemplate;

to

import org.axonframework.commandhandling.model.AggregateIdentifier;
import org.axonframework.eventsourcing.EventSourcingHandler;
import org.axonframework.commandhandling.CommandHandler;
import org.axonframework.mongo.eventsourcing.eventstore.MongoTemplate;

  • I updated my event sourcing repositories:
    @Bean(name = “attractionAggregateRepository”)
    public EventSourcingRepository attractionAggregateRepository() {
    EventSourcingRepository repository = new EventSourcingRepository<>(AttractionAggregate.class, eventStore, lockManager);
    repository.setEventBus(eventBus);

return repository;
}

to

@Bean(name = “attractionAggregateRepository”)
public Repository attractionAggregateRepository() {
return new EventSourcingRepository<>(AttractionAggregate.class, eventStore);
}

  • Updated my Spring bean config:
    @Bean(name = “commandBus”, destroyMethod = “shutdown”)
    public AsynchronousCommandBus commandBus() {
    AsynchronousCommandBus commandBus = new AsynchronousCommandBus();

List interceptors = new ArrayList<>(1);
interceptors.add(new BeanValidationInterceptor());
commandBus.setDispatchInterceptors(interceptors);

return commandBus;
}

@Bean(name = “commandGateway”)
public CommandGateway commandGateway() {
CommandGateway commandGateway = null;
CommandGatewayFactoryBean factory = new CommandGatewayFactoryBean();
factory.setCommandBus(commandBus());

// create a retry scheduler to re-run the command that failed with an exception
// I put this in place to contend with concurrency exceptions [BH]
ScheduledExecutorService ses = Executors.newScheduledThreadPool(1);
factory.setRetryScheduler(new IntervalRetryScheduler(ses, 500, 2));

try {
factory.afterPropertiesSet();
commandGateway = (CommandGateway) factory.getObject();
} catch (Exception e) {
throw new RuntimeException(e);
}

return commandGateway;
}

@Bean(name = “eventBus”)
public EventBus eventBus() {
EventBus eventBus = new SimpleEventBus();

return eventBus;
}

@Bean(name = “lockManager”)
public LockManager lockManager() {
PessimisticLockManager lockManager = new PessimisticLockManager(); // default Axon lock manager

return lockManager;
}

@Bean(name = “axonMongoTemplate”)
public AxonMongoTemplate axonMongoTemplate() {
return new AxonMongoTemplate(mongoDbFactory);
}

@Bean(name = “axonSagaMongoTemplate”)
public AxonSagaMongoTemplate mongoSagaTemplate() {
return new AxonSagaMongoTemplate(mongoDbFactory);
}

@Bean(name = “eventStore”)
public MongoEventStore eventStore() {
XStreamSerializer xStreamSerializer = new XStreamSerializer(xStream);

return new MongoEventStore(xStreamSerializer, axonMongoTemplate());
}

@Bean(name = “snapshotter”)
public Snapshotter snapshotter() {
SpringAggregateSnapshotter sas = new SpringAggregateSnapshotter();
sas.setEventStore(eventStore());
sas.setExecutor(taskExecutor);

return sas;
}

to

@Bean(name = “commandBus”)
public CommandBus commandBus() {
SimpleCommandBus commandBus = new SimpleCommandBus();

return commandBus;
}

@Bean(name = “commandGateway”)
public CommandGateway commandGateway() {
return new DefaultCommandGateway(commandBus());
}

@Bean(name = “eventBus”)
public EventBus eventBus() {
return new SimpleEventBus();
}

@Bean(name = “axonMongoTemplate”)
public AxonMongoTemplate axonMongoTemplate() {
return new AxonMongoTemplate(mongoDbFactory);
}

@Bean(name = “axonSagaMongoTemplate”)
public AxonSagaMongoTemplate mongoSagaTemplate() {
return new AxonSagaMongoTemplate(mongoDbFactory);
}

@Bean(name = “storageStrategy”)
public StorageStrategy storageStrategy() {
return new DocumentPerEventStorageStrategy();
}

@Bean(name = “xStreamSerializer”)
public XStreamSerializer xStreamSerializer() {
return new XStreamSerializer(xStream);
}

@Bean(name = “eventStore”)
public EventStore eventStore() {

return new EmbeddedEventStore(
new MongoEventStorageEngine(axonMongoTemplate(), storageStrategy())
);
}

  • There was no documentation or examples on the updated version of FutureCallback so I removed the few places I had been using it.
  • I updated all my command handler method to use the new callback lambda syntax:
    RateAggregate aggregate = new RateAggregate(command.getRateIdentifier(), command.getRate());
    rateAggregateRepository.add(aggregate);
    return command.getRateIdentifier();

to

return repository.newInstance(() ->
new RateAggregate(
command.getRateIdentifier(),
command.getRate()
)
).invoke(Function.identity());

Some takeaways from this:

  • I don’t know how to inject my custom xStreamSerializer anymore
  • FutureCallback needs a good a example
  • I can’t figure out how to configure SpringAggregateSnapshotter anymore

This is when everything started compiling again. I started the app but when my commands started dispatching they failed with errors stating that no Command Handlers could be found to catch those events. At that point, I started looking at the latest code to see if there were any clues as to whether the command handler instantiation had changed. That’s when I see the AxonSpringConfiguration class under quickstart and how repositories are instantiated differently. How you have @CommandHandler directly on the aggregate methods. How you instantiate the AggregateAnnotationCommandHandler. And the eventProcessor:

@Bean
public Repository repository(ParameterResolverFactory parameterResolverFactory) {
return new EventSourcingRepository<>(new GenericAggregateFactory<>(ToDoItem.class), eventStore(), parameterResolverFactory,
NoSnapshotTriggerDefinition.INSTANCE);
}

@Bean
public AggregateAnnotationCommandHandler aggregateAnnotationCommandHandler(ParameterResolverFactory parameterResolverFactory, Repository repository) {
AggregateAnnotationCommandHandler ch = new AggregateAnnotationCommandHandler<>(ToDoItem.class, repository, new AnnotationCommandTargetResolver(), parameterResolverFactory);
ch.subscribe(commandBus());
return ch;
}

@Bean
public ToDoEventHandler eventHandler() {
return new ToDoEventHandler();
}

@Bean
public SubscribingEventProcessor eventProcessor() {
SubscribingEventProcessor eventProcessor = new SubscribingEventProcessor(“eventProcessor”, new SimpleEventHandlerInvoker(eventHandler()), eventStore());
eventProcessor.start();
return eventProcessor;
}

At this point I couldn’t explore any further because there are classes here that don’t exist in M3 and I was failing to start the app. The last error I had before I gave up was:
Caused by: java.lang.NoSuchMethodError: com.thoughtworks.xstream.XStream.addImmutableType(Ljava/lang/Class;Z)V
at org.axonframework.serialization.AbstractXStreamSerializer.(AbstractXStreamSerializer.java:129) ~[axon-core-3.0-M3.jar:3.0-M3]
at org.axonframework.serialization.AbstractXStreamSerializer.(AbstractXStreamSerializer.java:103) ~[axon-core-3.0-M3.jar:3.0-M3]
at org.axonframework.serialization.AbstractXStreamSerializer.(AbstractXStreamSerializer.java:78) ~[axon-core-3.0-M3.jar:3.0-M3]
at org.axonframework.serialization.AbstractXStreamSerializer.(AbstractXStreamSerializer.java:67) ~[axon-core-3.0-M3.jar:3.0-M3]
at org.axonframework.serialization.xml.XStreamSerializer.(XStreamSerializer.java:49) ~[axon-core-3.0-M3.jar:3.0-M3]
at org.axonframework.eventsourcing.eventstore.AbstractEventStorageEngine.(AbstractEventStorageEngine.java:37) ~[axon-core-3.0-M3.jar:3.0-M3]
at org.axonframework.eventsourcing.eventstore.BatchingEventStorageEngine.(BatchingEventStorageEngine.java:40) ~[axon-core-3.0-M3.jar:3.0-M3]
at org.axonframework.eventsourcing.eventstore.mongo.MongoEventStorageEngine.(MongoEventStorageEngine.java:66) ~[axon-mongo-3.0-M3.jar:3.0-M3]
at com.traveliko.platform.persistence.mongo.config.AxonCQRSConfig.eventStore(AxonCQRSConfig.java:129) ~[traveliko-persistence-mongo-0.0.4-SNAPSHOT.jar:

maven_log_error.txt (6.71 KB)

Hi Bjorn,

the XStream error you’re getting is probably because you’re using a different (older) version of XStream. Axon has been built against 1.4.9.

The EventStore ‘design’ has changed a bit. You’ll need to instantiate an EmbeddedEventStore (currently the only available implementation) using a MongoEventStorageEngine. The StorageEngine has a setSerializer, in which you can configure a custom serializer. It defaults to XStream.

Note that the Configuration support in the master branch is work in progress. We’ll be providing support for Spring JavaConfig so that most of the beans don’t need to be explicitly defined anymore.

Then there’s the compile issue. I did a clean checkout and both the master branch as the axon-3.0-M3 tag seem to build fine. So I’m really in the dark about this one.
Could you share you ‘mvn -version’? Here’s mine:

Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-10T17:41:47+01:00)
Maven home: C:\Users\Allard\DevTools\apache-maven-3.3.9
Java version: 1.8.0_102, vendor: Oracle Corporation
Java home: C:\Program Files\Java\jdk1.8.0_102\jre
Default locale: en_US, platform encoding: Cp1252
OS name: “windows 10”, version: “10.0”, arch: “amd64”, family: “dos”

Cheers,

Allard

Hi Allard,

Here are my Maven details:
`

crash:traveliko-backend crash$ mvn --version
Java HotSpot™ 64-Bit Server VM warning: ignoring option MaxPermSize=1024m; support was removed in 8.0
Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-10T23:41:47+07:00)
Maven home: /opt/maven
Java version: 1.8.0_102, vendor: Oracle Corporation
Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0_102.jdk/Contents/Home/jre
Default locale: en_US, platform encoding: UTF-8
OS name: “mac os x”, version: “10.11.6”, arch: “x86_64”, family: “mac”
`

Yes, you are right. IntelliJ is not very good moving between Git branches and getting dependencies updated from the pom files.

The error I am getting now when trying to start up is:
`

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.axonframework.commandhandling.model.Repository]: Factory method ‘roleAggregateRepository’ threw exception; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
… 59 more
Caused by: java.lang.NullPointerException
at org.axonframework.commandhandling.model.AbstractMessageHandler.(AbstractMessageHandler.java:45) ~[axon-core-3.0-M3.jar:3.0-M3]
at org.axonframework.commandhandling.model.inspection.MethodCommandHandlerDefinition$MethodCommandMessageHandler.(MethodCommandHandlerDefinition.java:54) ~[axon-core-3.0-M3.jar:3.0-M3]
at org.axonframework.commandhandling.model.inspection.MethodCommandHandlerDefinition.createHandler(MethodCommandHandlerDefinition.java:40) ~[axon-core-3.0-M3.jar:3.0-M3]
at org.axonframework.common.annotation.AnnotatedHandlerInspector.lambda$initializeMessageHandlers$143(AnnotatedHandlerInspector.java:86) ~[axon-core-3.0-M3.jar:3.0-M3]
at java.util.ArrayList.forEach(ArrayList.java:1249) ~[?:1.8.0_102]
at org.axonframework.common.annotation.AnnotatedHandlerInspector.initializeMessageHandlers(AnnotatedHandlerInspector.java:85) ~[axon-core-3.0-M3.jar:3.0-M3]
at org.axonframework.common.annotation.AnnotatedHandlerInspector.initialize(AnnotatedHandlerInspector.java:67) ~[axon-core-3.0-M3.jar:3.0-M3]
at org.axonframework.common.annotation.AnnotatedHandlerInspector.lambda$createInspector$139(AnnotatedHandlerInspector.java:53) ~[axon-core-3.0-M3.jar:3.0-M3]
at java.util.HashMap.computeIfAbsent(HashMap.java:1126) ~[?:1.8.0_102]
at org.axonframework.common.annotation.AnnotatedHandlerInspector.createInspector(AnnotatedHandlerInspector.java:53) ~[axon-core-3.0-M3.jar:3.0-M3]
at org.axonframework.common.annotation.AnnotatedHandlerInspector.inspectType(AnnotatedHandlerInspector.java:47) ~[axon-core-3.0-M3.jar:3.0-M3]
at org.axonframework.commandhandling.model.inspection.ModelInspector.inspectAggregate(ModelInspector.java:65) ~[axon-core-3.0-M3.jar:3.0-M3]
at org.axonframework.commandhandling.model.inspection.ModelInspector.inspectAggregate(ModelInspector.java:58) ~[axon-core-3.0-M3.jar:3.0-M3]
at org.axonframework.commandhandling.model.AbstractRepository.(AbstractRepository.java:57) ~[axon-core-3.0-M3.jar:3.0-M3]
at org.axonframework.commandhandling.model.LockingRepository.(LockingRepository.java:80) ~[axon-core-3.0-M3.jar:3.0-M3]
at org.axonframework.commandhandling.model.LockingRepository.(LockingRepository.java:60) ~[axon-core-3.0-M3.jar:3.0-M3]
at org.axonframework.eventsourcing.EventSourcingRepository.(EventSourcingRepository.java:69) ~[axon-core-3.0-M3.jar:3.0-M3]
at org.axonframework.eventsourcing.EventSourcingRepository.(EventSourcingRepository.java:57) ~[axon-core-3.0-M3.jar:3.0-M3]
at com.traveliko.platform.domain.user.config.UserConfig.roleAggregateRepository(UserConfig.java:56) ~[traveliko-domain-user-0.0.4-SNAPSHOT.jar:?]
`

And it’s complaining because of this:

@Bean(name = "roleAggregateRepository") public Repository<RoleAggregate> roleAggregateRepository() { return new EventSourcingRepository<>(RoleAggregate.class, eventStore); }

I am going to wait until the Configuration support is out because I have too many CommandHandlers to specify manually. I also know that you guys are changing the signature for event sourcing repos and that might be why the above mentioned breaks.

Looking forward to a release candidate! :slight_smile:

Hi Bjorn,

the nullpointer is caused by an issue in the AnnotatedMessage handler. It’s not really a bug, but just an implementation with very poor way of saying what’s wrong ;-).

The problem is that one of your @EventHandler of @CommandHandler annotated methods has a parameter that Axon doesn’t have a resolver for. Usually, the first parameter is the payload, and any additional parameters may indicate other information Axon should inject. Do you use this feature?

Cheers,

Allard

Hi Bjorn,

I just asked a colleague with a MacBook to compile the current Axon master. He’s running the same maven and java versions and everything compiles just nicely.
Maybe it’s a commit that happened after the one you checked out, but it might be something else as well. Anyway, I’m planning to do a M4 release early next week, so you should have all the fixed stuff available to you anyway.

Cheers,

Allard

Hi Allard,

I haven’t made any changes to my method signatures and this is working in 2.x. I am assuming this fails because I haven’t declared everything in my JavaConfig yet.

Will wait for M4 to see what the new Configuration code looks like.

Cheers