Snapshot woes

Hiya

I’m having an issue with creating a snapshot in Axon v3.1.

Brian Sanders was kind enough to provide some sample code, and I’ve just extended it to try and introduce support for snapshots.

The snapshot code was taken from some code I had working in Axon v2.

The error I am getting is that when I request a snapshot I get the following error:

An attempt to create and store a snapshot resulted in an exception. Exception summary: Unable to resolve parameter 1 (PasswordHashGenerator) in handler public void com.sample.user.UserAggregate.authenticateUser(com.sample.user.commands.AuthenticateUser,com.sample.user.crypto.PasswordHashGenerator)

The method it is having problems with is defined as follows:

@CommandHandler
public void authenticateUser(AuthenticateUser cmd, PasswordHashGenerator passwordHashGenerator) {
    if (passwordHashGenerator.validatePasswordHash(cmd.getPassword(), getPasswordHash())) {
        apply(new UserAuthenticated(getId()));
    } else {
        apply(new UserAuthenticationFailed(getId()));
    }
}

The snapshotter class is defined as follows:

@Slf4j
@Component
public class UserSnapshotter {

    @Autowired
    private AggregateSnapshotter snapshotter;

    @Autowired
    private EventStore store;

    public void createSnapshot(String aggregateIdentifier) {

        snapshotter.scheduleSnapshot(UserAggregate.class, aggregateIdentifier);
        log.info("SCHEDULED A SNAPSHOT!");

    }

}

and it’s called from the Web controller as follows:

@Autowired
private UserSnapshotter shotter;
@PutMapping(path = "{id}/snapshot")
public void snapshot(@PathVariable String id) {

    shotter.createSnapshot(id);

}

The full source code is in this repository here: https://github.com/deevodavis71/sample-user-service

It’s important to me that I can use Spring Boot with Axon as we already have an extensive set of code using Spring, and if I’m to convince my team to use Event Sourcing and CQRS then to not use Spring would be a step too far…

Any ides how I get around the issue?

Kind regards
Steve

Hello Steve,
I don’t have an answer, but I just wanted to say Thanks! for the credit and I’m glad to hear that code I wrote to help me learn about something new helped you as well.

Hi,

the problem is that Axon can’t find a suitable instance of PasswordHashGenerator to inject.
If uou’re on Spring (Boot), you can define it as a bean, and Axon will inject it. Note that candidates are only eligible if there is a single instance.

Hope this helps,

Cheers,

Allard

Hi Allard

Thanks for your response.

That particular reference was already defined as a @Bean in the main SpringBootApplication annotated class, however it was implementing an interface that was itself extended from another.

@Bean
@ConditionalOnMissingBean
public PasswordHashGenerator passwordHashGenerator() {
    return new PBKDF2SaltedPasswordHashGenerator();
}

To simplify things I made the bean class implement only a single-level interface (rather than the inherited one it originally did), and there is only a single instance implementing that interface as you suggest. That meant I could also get rid of the @ConditionalOnMissingBean annotation so it now reads simply

@Bean
public PasswordHashGenerator passwordHashGenerator() {
    return new PBKDF2SaltedPasswordHashGenerator();
}

The exception is still being thrown when I try and schedule a snapshot, but it now reads as follows: An attempt to create and store a snapshot resulted in an exception. Exception summary: null

I’m sorry, but I’m stuck again as it’s all a little bit of a black box.

I’ve updated the GitHub repo mentioned in my original post accordingly, if anyone has time to take a look and tell me what I’ve done wrong!

Cheers Steve

Hi Steve,

The null bit in your exception is the message from the exception being thrown by the Snapshotting Task.

It being null obviously doesn’t simplify the process to tracking down what’s failing in your situation.

So to give you an exact reference of the line of code:

AbstractSnapshotter, lines 138 - 152
@Override
public void run() {
try {
snapshotterTask.run();
} catch (ConcurrencyException e) {
logger.info(“An up-to-date snapshot entry already exists, ignoring this attempt.”);
} catch (Exception e) {
if (logger.isDebugEnabled()) {
logger.warn(“An attempt to create and store a snapshot resulted in an exception:”, e);
} else {
logger.warn("An attempt to create and store a snapshot resulted in an exception. " +
“Exception summary: {}”, e.getMessage()); // <— This logger is hit, and e.getMessage is null
}
}
}

Do you per change have a more elaborate stack trace?
Maybe we can distille something from that.

Cheers,

Steven

Hi Steven

After getting back home today I turned on debug logging and rather than the “null” exception error I mentioned I now get the same original message about being unable to resolve the parameter.

I tried turning off logging again and still get the same “unable to resolve parameter” exception so can only think the “null” message was a wierdity that occurred somehow!

Anyway, the full stacktrace is shown below.

I have wired in an instance of the PasswordHashGenerator into the web controller and can find and use it OK in a different REST method, so it’s definitely there as a Spring bean, it just seems that Axon cannot find it …

Kind regards
Steve

2018-02-26 16:46:15.817 WARN 35178 — [nio-8081-exec-5] o.a.eventsourcing.AbstractSnapshotter : An attempt to create and store a snapshot resulted in an exception:

org.axonframework.messaging.annotation.UnsupportedHandlerException: Unable to resolve parameter 1 (PasswordHashGenerator) in handler public void com.sample.user.UserAggregate.authenticateUser(com.sample.user.commands.AuthenticateUser,com.sample.user.crypto.PasswordHashGenerator).
at org.axonframework.messaging.annotation.AnnotatedMessageHandlingMember.(AnnotatedMessageHandlingMember.java:65) ~[axon-core-3.1.jar:3.1]
at org.axonframework.messaging.annotation.AnnotatedMessageHandlingMemberDefinition.lambda$createHandler$0(AnnotatedMessageHandlingMemberDefinition.java:51) ~[axon-core-3.1.jar:3.1]
at java.util.Optional.map(Optional.java:215) ~[na:1.8.0_131]
at org.axonframework.messaging.annotation.AnnotatedMessageHandlingMemberDefinition.createHandler(AnnotatedMessageHandlingMemberDefinition.java:48) ~[axon-core-3.1.jar:3.1]
at org.axonframework.messaging.annotation.AnnotatedHandlerInspector.lambda$initializeMessageHandlers$1(AnnotatedHandlerInspector.java:108) ~[axon-core-3.1.jar:3.1]
at java.util.ArrayList.forEach(ArrayList.java:1249) ~[na:1.8.0_131]
at org.axonframework.messaging.annotation.AnnotatedHandlerInspector.initializeMessageHandlers(AnnotatedHandlerInspector.java:108) ~[axon-core-3.1.jar:3.1]
at org.axonframework.messaging.annotation.AnnotatedHandlerInspector.initialize(AnnotatedHandlerInspector.java:98) ~[axon-core-3.1.jar:3.1]
at org.axonframework.messaging.annotation.AnnotatedHandlerInspector.createInspector(AnnotatedHandlerInspector.java:79) ~[axon-core-3.1.jar:3.1]
at org.axonframework.messaging.annotation.AnnotatedHandlerInspector.inspectType(AnnotatedHandlerInspector.java:71) ~[axon-core-3.1.jar:3.1]
at org.axonframework.commandhandling.model.inspection.AnnotatedAggregateMetaModelFactory.createModel(AnnotatedAggregateMetaModelFactory.java:97) ~[axon-core-3.1.jar:3.1]
at org.axonframework.commandhandling.model.inspection.AnnotatedAggregateMetaModelFactory.inspectAggregate(AnnotatedAggregateMetaModelFactory.java:87) ~[axon-core-3.1.jar:3.1]
at org.axonframework.eventsourcing.AggregateSnapshotter.lambda$createSnapshot$2(AggregateSnapshotter.java:123) ~[axon-core-3.1.jar:3.1]
at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660) ~[na:1.8.0_131]
at org.axonframework.eventsourcing.AggregateSnapshotter.createSnapshot(AggregateSnapshotter.java:123) ~[axon-core-3.1.jar:3.1]
at org.axonframework.eventsourcing.AbstractSnapshotter$CreateSnapshotTask.run(AbstractSnapshotter.java:170) ~[axon-core-3.1.jar:3.1]
at org.axonframework.common.transaction.TransactionManager.executeInTransaction(TransactionManager.java:44) ~[axon-core-3.1.jar:3.1]
at org.axonframework.eventsourcing.AbstractSnapshotter.lambda$scheduleSnapshot$0(AbstractSnapshotter.java:86) [axon-core-3.1.jar:3.1]
at org.axonframework.eventsourcing.AbstractSnapshotter$SilentTask.run(AbstractSnapshotter.java:141) ~[axon-core-3.1.jar:3.1]
at org.axonframework.common.DirectExecutor.execute(DirectExecutor.java:42) [axon-core-3.1.jar:3.1]
at org.axonframework.eventsourcing.AbstractSnapshotter.scheduleSnapshot(AbstractSnapshotter.java:85) [axon-core-3.1.jar:3.1]
at com.sample.user.snapshot.UserSnapshotter.createSnapshot(UserSnapshotter.java:23) [classes/:na]
at com.sample.user.web.UserController.snapshot(UserController.java:78) [classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_131]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_131]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_131]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_131]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) [spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133) [spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97) [spring-webmvc-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) [spring-webmvc-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) [spring-webmvc-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) [spring-webmvc-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963) [spring-webmvc-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) [spring-webmvc-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) [spring-webmvc-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:883) [spring-webmvc-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:664) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) [spring-webmvc-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) [tomcat-embed-websocket-8.5.14.jar:8.5.14]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:105) [spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81) [spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) [spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455) [tomcat-embed-core-8.5.14.jar:8.5.14]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.14.jar:8.5.14]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_131]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_131]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.14.jar:8.5.14]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_131]

2018-02-26 16:46:15.818 INFO 35178 — [nio-8081-exec-5] c.sample.user.snapshot.UserSnapshotter : SCHEDULED A SNAPSHOT!

Hi Steve,

Sorry for the somewhat late reply, had quite a busy week.

Now let’s go back to your exception.

The fact you’ve got the ‘Unable to resolve parameter’ exception again, suggests that that is the culprit why snapshotting isn’t working.

I just now took a look at the sample project your shared, which gave me enough info to point you in the right direction.

The AggregateSnapshotter you’ve created in your UserServiceApplication class does not have a ParamaterResolverFactory.

Without one, Axon is not able to resolve the Spring Beans in your message handling functions.

Thus, you can either wire it yourself into the AggregateSnapshotter you’re creating, or you can leverage the SpringAggregateSnapshotterFactoryBean.

When using the latter. you can do SpringAggregateSnapshotterFactoryBean#getObject and the factory bean will create a SpringAggregateSnapshotter for you with the right parameters wired (thus automatically wiring the right ParameterResolverFactory.

Hope this helps you out!

Cheers,

Steven