Hi!
I’m using Axon 3.07 with SpringAxonAutoConfigurer and I’m having troubles setting up custom Repositories.
Whenever I manually configure a Repository bean, I get an exception that my additional command handler parameters cannot be injected in the Aggregate.
Everything works fine without manually specifying a repository though. The reason I need a custom repository bean is to configure Snapshotting.
By the way, it seems that in my case SpringAxonAutoConfigurer registers beans with their full-qualified name instead of their simple name. Hence, if I don’t manually specify the repository name in the Aggregate annotation, it looks for a repository with the Name “com.my.sample.calendar.CalendarRepository” instead of “calendarRepository”.
This is the exception I receive:
`
WARN 60464 — [ main] ationConfigEmbeddedWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘calendarRepository’ defined in class path resource [com/my/sample/configuration/AxonConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.axonframework.commandhandling.model.Repository]: Factory method ‘calendarRepository’ threw exception; nested exception is org.axonframework.messaging.annotation.UnsupportedHandlerException: Unable to resolver parameter 2 (CalendarDao) in handler public com.my.sample.calendar.Calendar(com.my.sample.calendar.command.CreateCalendar,org.axonframework.messaging.MetaData,com.my.sample.calendar.CalendarDao).
`
Here is my Aggregate:
`
@Aggregate(repository = “calendarRepository”)
public class Calendar {
@AggregateIdentifier
private UUID id;
private String name;
private List days;
private Integer calendarIdentifier;
// no arg constructor for axon
public Calendar() {
}
@CommandHandler
public Calendar(CreateCalendar cmd, MetaData metadata, CalendarDao dao) {
Integer calendarIdentifier = dao.claimCalendarId(cmd.getId()).orElseThrow(CalendarLimitReachedException::new);
dao.claimCalendarName(cmd.getId(), cmd.getName());
apply(CalendarCreated.builder()
.id(cmd.getId())
.calendarIdentifier(calendarIdentifier)
.name(cmd.getName())
.days(cmd.days())
.build(), metadata);
}
@EventSourcingHandler
public void imposeCreated(CalendarCreated evt) {
this.id = evt.getId();
this.calendarIdentifier = evt.getCalendarIdentifier();
this.name = evt.getName();
this.days = evt.getDays();
}
/*
…
*/
}
`
And here is my configuration:
`
@Configuration
@EnableAxon
public class AxonConfiguration {
@Autowired
private ObjectMapper objectMapper;
@Autowired
private DataSourceTransactionManager platformTransactionManager;
@Autowired
private DataSource dataSource;
@Autowired
public void configure(EventHandlingConfiguration config, Environment env) {
if (!Arrays.asList(env.getActiveProfiles()).contains(“test”)
&& !Arrays.asList(env.getActiveProfiles()).contains(“localtest”)) {
config.usingTrackingProcessors(); // default all processors to tracking mode.
}
}
@Bean
public CustomCommandGateway customCommandGateway(CommandBus commandBus) {
return new CommandGatewayFactory(commandBus).createGateway(CustomCommandGateway.class);
}
//====================================
//====================================
// Storage init
//====================================
//====================================
@Bean
public Serializer serializer(ObjectMapper axonObjectMapper) {
// The axon object mapper is configured in the ObjectMapperConfiguration class.
return new JacksonSerializer(axonObjectMapper);
}
@Bean
public EventUpcasterChain upcasterChain() {
// Add Upcaster constructor calls here
return new EventUpcasterChain(
);
}
@Bean
public PersistenceExceptionResolver persistenceExceptionResolver() {
return new JdbcSQLErrorCodesResolver();
}
@Bean
public UnitOfWorkAwareConnectionProviderWrapper springDataSourceConnectionProvider(DataSource dataSource) {
return new UnitOfWorkAwareConnectionProviderWrapper(new SpringDataSourceConnectionProvider(dataSource));
}
@Bean
public JdbcEventStorageEngine jdbcEventStorageEngine(Serializer serializer,
EventUpcasterChain upcasterChain,
PersistenceExceptionResolver persistenceExceptionResolver,
ConnectionProvider connectionProvider,
TransactionManager transactionManager) {
return new JdbcEventStorageEngine(serializer, upcasterChain, persistenceExceptionResolver,
connectionProvider, transactionManager);
}
@Bean
public JdbcTokenStore jdbcTokenStore(ConnectionProvider springDataSourceConnectionProvider, Serializer serializer) {
return new JdbcTokenStore(springDataSourceConnectionProvider, serializer);
}
@Bean
public SpringAggregateSnapshotterFactoryBean springAggregateSnapshotterFactoryBean() {
return new SpringAggregateSnapshotterFactoryBean();
}
@Bean
public EventCountSnapshotTriggerDefinition eventCountSnapshotterTrigger(AggregateSnapshotter snapshotter) {
return new EventCountSnapshotTriggerDefinition(snapshotter, 100);
}
@Bean
public SagaStore sagaRepository(ConnectionProvider springDataSourceConnectionProvider, Serializer serializer) {
// Use the Generic Schema, since it stores the Saga data in a single column instead of using n key/value rows per Saga.
JdbcSagaStore jdbcStore = new JdbcSagaStore(springDataSourceConnectionProvider, new GenericSagaSqlSchema(), serializer);
return new CachingSagaStore<>(jdbcStore, new WeakReferenceCache(), new WeakReferenceCache());
}
@Bean
public Repository calendarRepository(EventStore eventStore, Snapshotter snapshotter) {
EventCountSnapshotTriggerDefinition snapshotTrigger = new EventCountSnapshotTriggerDefinition(snapshotter, 100);
return new EventSourcingRepository<>(Calendar.class, eventStore, snapshotTrigger);
}
//====================================
//====================================
// Individual Saga Configuration
//====================================
//====================================
@Bean
public SagaConfiguration updateCalendarSagaConfiguration() {
return SagaConfiguration.trackingSagaManager(UpdateCalendarSaga.class);
}
@Bean
public SagaConfiguration prepareRemovalSagaConfiguration() {
return SagaConfiguration.trackingSagaManager(PrepareRemovalSaga.class);
}
}
`
Thanks a lot!
Andreas