Axon 4 documentation

Hello,

currently I am trying to start a new Spring Boot project with Axon 4.

I have some trouble to set everything up and get my project working by
following the documentation.

Seems like the docs are not up to date (or maybe just missing a few things).

For example, the documentation says Axon is by default configured to work
Axon Server.

When starting Axon Server and running my simple hello world application I
just get the following error:

Servlet.service() for servlet [dispatcherServlet] in context with path []
threw exception [Request processing failed; nested exception is
AxonServerRemoteCommandHandlingException{message=An exception was thrown by
the remote message handling component., errorCode='AXONIQ-4000',
server='AxonHub'}] with root cause

org.axonframework.axonserver.connector.command.AxonServerRemoteCommandHandlingException:
An exception was thrown by the remote message handling component.

                at
org.axonframework.axonserver.connector.command.AxonServerCommandBus$2.onNext(AxonServerCommandBus.java:139)
~[axon-server-connector-4.0.jar:4.0]

                at
org.axonframework.axonserver.connector.command.AxonServerCommandBus$2.onNext(AxonServerCommandBus.java:116)
~[axon-server-connector-4.0.jar:4.0]

                at
io.grpc.stub.ClientCalls$StreamObserverToCallListenerAdapter.onMessage(ClientCalls.java:407)
~[grpc-stub-1.13.1.jar:1.13.1]

                at
io.grpc.ForwardingClientCallListener.onMessage(ForwardingClientCallListener.java:33)
~[grpc-core-1.13.1.jar:1.13.1]

                at
io.grpc.ForwardingClientCallListener.onMessage(ForwardingClientCallListener.java:33)
~[grpc-core-1.13.1.jar:1.13.1]

                at
io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1MessagesAvailable.runInContext(ClientCallImpl.java:519)
~[grpc-core-1.13.1.jar:1.13.1]

                at
io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
~[grpc-core-1.13.1.jar:1.13.1]

                at
io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123)
~[grpc-core-1.13.1.jar:1.13.1]

                at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
~[na:1.8.0_192]

                at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
~[na:1.8.0_192]

                at java.lang.Thread.run(Thread.java:748) [na:1.8.0_192]

In the next step, I tried to use the MongoDB event store. Unfortunately I
wasn’t able to figure out how to configure Axon with MongoDB, since the
documentation doesn’t give instructions on how to do this.

So, I am currently stuck.

Are there any working code examples for Axon 4 I could use as a reference?

Kind regards

Alexander

Hi,

the documentation is indeed being worked on, at the moment. We’re doing the best we can to update it as soon as possible.
In the meantime, there is a quick-start project to help you setting up an axon application, taking you through the process, step by step.
https://github.com/AxonIQ/axon-quick-start

You can also have a look at the sample application:
https://github.com/AxonIQ/giftcard-demo

Hope this helps.
Cheers,

Allard

I noticed that @EnableAxon was removed in 4 (no surprise). I tried replacing it with @Import(SpringAxonAutoConfigurer.ImportSelector.class) @AnnotationDriven on my class, but I get an error which is related to the fact that I am using Spring 4 and the missing function is in Spring 5.

java.lang.NoSuchMethodError: org.springframework.beans.factory.support.BeanDefinitionBuilder.genericBeanDefinition(Ljava/lang/Class;Ljava/util/function/Supplier;)Lorg/springframework/beans/factory/support/BeanDefinitionBuilder;
at org.axonframework.spring.config.SpringAxonAutoConfigurer.registerBeanDefinitions(SpringAxonAutoConfigurer.java:195)

The sample code at https://docs.axoniq.io/reference-guide/2-axon-server/connecting-axon-apps is also outdated, but the takeaway is “you need to configure the various buses manually”, so there’s that.

In short, I was looking for some direction in getting my Spring Non-Boot connected to Axon Server. Thanks!

While I would still appreciate an answer (not necessarily from you), it seems that I will need to upgrade to Spring 5.1.4 (because of Spring Security) at the very least.

Hi Brian,

Axon doesn’t rely on Spring 5. Spring 4 should work equally well.

Note that “auto configuration” is not really supported anymore, unless Spring Autoconfiguration is available, which is the case when you use Spring Boot. While you can probably find some workarounds (importing yourself), we’ve noticed that it’s very easy to get into timing issues when initializing beans.

You can create a bean of type AxonServerEventStore. You can create an instance of it using the builder().(do some initialization)… .build() methods. You’ll need to pass some configuration and a ConnectionManager. Those are beans that you also need to define.

As either using the Configuration API (in non-Spring environments) or the Spring Boot autoconfiguration are the preferred methods of setting Axon up, we haven’t described the other methods in our reference guide, yet.
Hope these pointers help you further, though.

Cheers,

Hi Brian,

Our current reference guide (Introduction - Axon Reference Guide) covers two configuration flavors (as Allard already mentioned):

Fill free to explore how Axon declared all needed Spring beens in auto-configuration (AxonFramework/spring-boot-autoconfigure/src/main/java/org/axonframework/springboot/autoconfig at master · AxonFramework/AxonFramework · GitHub). This can be a nice place to start with planing your manual Spring configuration.

Most interesting auto-configuration parts are:

Cheers,
Ivan

Allard/Ivan,
Thanks for that information. I noticed that the reference guide has been updated since I originally posted so I will go ahead and look it over. Is axon-spring only applicable for boot applications? The description makes it sound like I can include it in my app, but there are other things I see that make me unsure.

I would like to say thank you for the updated Axon 4 reference guide. As someone new to Axon I find myself referencing Axon 3 and 2 documentation far too often.

Hey Brian,

axon-spring is not applicable for spring boot applications only.

I believe that there is a demo application Axon Trader (https://github.com/AxonFramework/Axon-trader/blob/master/web-ui/src/main/java/org/axonframework/samples/trader/webui/config/AppConfig.java) that still use this module to manually configure Spring components. You can have a look there as well. Please note that this demo is old-ish, but functioning as expected. It uses axon 3.3.1 version. Hope this helps.

Best,
Ivan

Hello Michael,

We will continue enhancing our reference guide, and your feedback is very much appreciated. Thank you.

Best,
Ivan

Ok, still trying to figure this out. Just want to share my experience so far…

DefaultConfigurer.defaultConfiguration() triggers ServerConnectorConfigurerModule.configureModule(Configurer). In there is the line: configurer.registerComponent(AxonServerConfiguration.class, c -> new AxonServerConfiguration()); which just uses the class defaults for a non-Spring Boot app that don’t work for me because I’m using the axon-server docker image.

Ideally, being able to somehow pass in my own config would be nice, but I’m not sure how that would be possible.

axon-spring 4.0.3 depends on Spring 5.1.1 which is what prompted my initial question. So, I would like to rephrase: Can axon-spring 4.0.3 work with my Spring 4 app?

Below is code I took from ServerConnectorConfigurerModule and attempted to Spring-ify it, but I have not figured out a way to declare the buses as beans.

Am I going to have to manually register the components in the Spring context?
Prior to the upgrade, I had a simple subscribing listener that was automatically registered to Axon. Am I going to have to manually register the listener now?

Thanks

`
@org.springframework.context.annotation.Configuration
public class AxonConfiguration {

private Logger logger = LoggerFactory.getLogger(getClass());

public AxonConfiguration() {
logger.info(“Loading Axon Configuration”);
}

@Bean(destroyMethod=“shutdown”)
public Configuration configuration(AxonServerConfiguration axonServerConfiguration, AxonServerConnectionManager axonServerConnectionManager) {
return DefaultConfigurer.defaultConfiguration()
.registerComponent(AxonServerConfiguration.class, c -> axonServerConfiguration)
.registerComponent(AxonServerConnectionManager.class, c -> {
c.onShutdown(axonServerConnectionManager::shutdown);
return axonServerConnectionManager;
})
.configureEventStore(this::buildEventStore)
.configureCommandBus(this::buildCommandBus)
.configureQueryBus(this::buildQueryBus)
.start();
}

@Bean
public AxonServerConfiguration axonServerConfiguration(Properties applicationConfig) {
return AxonServerConfiguration.builder()
.componentName(applicationConfig.getProperty(“axon.axonserver.componentName”, “PM”))
.servers(applicationConfig.getProperty(“axon.axonserver.servers”, “localhost”))
.token(applicationConfig.getProperty(“axon.axonserver.token”, “”))
.build();
}

@Bean(initMethod=“getChannel”)
public AxonServerConnectionManager axonServerConnectionManager(AxonServerConfiguration c) {
return new AxonServerConnectionManager©;
}

public AxonServerEventStore buildEventStore(Configuration c) {
return AxonServerEventStore.builder()
.configuration(c.getComponent(AxonServerConfiguration.class))
.platformConnectionManager(c.getComponent(AxonServerConnectionManager.class))
.snapshotSerializer(c.serializer())
.eventSerializer(c.eventSerializer())
.upcasterChain(c.upcasterChain())
.build();
}

public AxonServerCommandBus buildCommandBus(Configuration c) {
AxonServerCommandBus commandBus = new AxonServerCommandBus(c.getComponent(AxonServerConnectionManager.class),
c.getComponent(AxonServerConfiguration.class),
SimpleCommandBus.builder().build(),
c.messageSerializer(),
c.getComponent(RoutingStrategy.class, AnnotationRoutingStrategy::new),
c.getComponent(CommandPriorityCalculator.class,
() -> new CommandPriorityCalculator() {}));
c.onShutdown(commandBus::disconnect);
return commandBus;
}

public QueryBus buildQueryBus(Configuration c) {
SimpleQueryBus localSegment = SimpleQueryBus.builder()
.transactionManager(c.getComponent(TransactionManager.class, NoTransactionManager::instance))
.errorHandler(c.getComponent(QueryInvocationErrorHandler.class, () -> LoggingQueryInvocationErrorHandler.builder().build()))
.queryUpdateEmitter(c.queryUpdateEmitter())
.messageMonitor(c.messageMonitor(QueryBus.class, “localQueryBus”))
.build();

AxonServerQueryBus queryBus = new AxonServerQueryBus(c.getComponent(AxonServerConnectionManager.class),
c.getComponent(AxonServerConfiguration.class),
c.queryUpdateEmitter(),
localSegment, c.messageSerializer(), c.serializer(), c.getComponent(QueryPriorityCalculator.class, () -> new QueryPriorityCalculator() {}));

c.onShutdown(queryBus::disconnect);

return queryBus;
}
}

`

Hi Brian,

Axon 4 should be compatible with Spring 4. The fact that it uses Spring 5 internally does restrict you in any way. Spring has maintain quite a lot of compatibility between the different versions.

It is still possible to leverage the “auto configuration” even if you’re not on Spring Boot. It is however a deprecated feature that we expect to remove in upcoming versions. Before definitively removing it, we want to restructure the autoconfiguration component, so that configuring Axon through Spring without AutoConfiguration is a bit easier.

You can add @Import(SpringAxonAutoConfigurer.ImportSelector.class) on one of your configuration files. It configures a Configuration class that defines a number of sensible defaults.

Hope this helps.
Cheers,