Hey everyone,
I had to implement a custom Spring Boot application that implements multiple datasources.
This prevents me from using the Axon auto configuration for Spring Boot, meaning I have to provide Axon with all the required beans, starting from a standard DataSource which I want axon to use.
@Autowired
public void configure(
EventProcessingConfigurer config, AxonPropagatingErrorHandler errorHandler) {
config.registerDefaultListenerInvocationErrorHandler(conf -> errorHandler);
}
@Bean
public IdentifierFactory getIdentifierFactory() {
return IdentifierFactory.getInstance();
}
@Bean
@ConfigurationProperties(prefix = "custom.datasource")
DataSource dataSource() {
return DataSourceBuilder.create().build();
}
/************************************************************************
* Configuring JPA
************************************************************************/
public JpaProperties customJpaProperties() {
final JpaProperties properties = new JpaProperties();
properties.setProperties(jpaProperties());
return properties;
}
@Bean
public EntityManagerFactoryBuilder entityManagerFactoryBuilder() {
return new EntityManagerFactoryBuilder(
new HibernateJpaVendorAdapter(), jpaProperties(), null);
}
@Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder, DataSource dataSource) {
return builder.dataSource(dataSource)
.properties(jpaProperties())
.packages(
"org.axonframework.eventhandling.tokenstore",
"org.axonframework.eventsourcing.eventstore.jpa",
"org.axonframework.modelling.saga.repository.jpa")
.persistenceUnit("punit")
.build();
}
@Bean
PlatformTransactionManager platformTransactionManager(
EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
@Bean
EntityManager sharedEntityManager(
EntityManagerFactory entityManagerFactory) {
return SharedEntityManagerCreator.createSharedEntityManager(entityManagerFactory);
}
/************************************************************************
* Axon Framework specific things
************************************************************************/
@Bean
EntityManagerProvider entityManagerProvider(
EntityManager entityManager) {
return new SimpleEntityManagerProvider(entityManager);
}
@Bean
TransactionManager transactionManager(
PlatformTransactionManager transactionManager) {
return new SpringTransactionManager(transactionManager);
}
@Bean
PersistenceExceptionResolver dataSourcePER(DataSource dataSource)
throws SQLException {
return new SQLErrorCodesResolver(dataSource);
}
@Bean
@Primary
EventStorageEngine storageEngine(
Serializer eventSerializer,
Serializer snapshotSerializer,
DataSource dataSource,
EntityManagerProvider entityManagerProvider,
PersistenceExceptionResolver persistenceExceptionResolver,
TransactionManager transactionManager)
throws SQLException {
return JpaEventStorageEngine.builder()
.eventSerializer(eventSerializer)
.snapshotSerializer(snapshotSerializer)
.dataSource(dataSource)
.entityManagerProvider(entityManagerProvider)
.persistenceExceptionResolver(persistenceExceptionResolver)
.transactionManager(transactionManager)
.build();
}
@Bean
public PersistenceExceptionResolver dataSourcePersistenceExceptionResolver(
DataSource dataSource) throws SQLException {
return new SQLErrorCodesResolver(dataSource);
}
@ConditionalOnMissingBean({
DataSource.class,
PersistenceExceptionResolver.class,
EventStore.class
})
@Bean
public PersistenceExceptionResolver jdbcSQLErrorCodesResolver() {
return new JdbcSQLErrorCodesResolver();
}
@Bean
public ConnectionProvider connectionProvider(DataSource dataSource) {
return new UnitOfWorkAwareConnectionProviderWrapper(
new SpringDataSourceConnectionProvider(dataSource));
}
@Bean
public TokenStore tokenStore(ConnectionProvider connectionProvider, Serializer serializer) {
return JdbcTokenStore.builder()
.connectionProvider(connectionProvider)
.serializer(serializer)
.build();
}
@Bean
public JdbcSagaStore sagaStore(
ConnectionProvider connectionProvider, Serializer serializer) {
return JdbcSagaStore.builder()
.connectionProvider(connectionProvider)
.sqlSchema(new GenericSagaSqlSchema())
.serializer(serializer)
.build();
}
@Bean
@Primary
JpaSagaStore sagaStore(
Serializer serializer, EntityManagerProvider entityManagerProvider) {
return JpaSagaStore.builder()
.entityManagerProvider(entityManagerProvider)
.serializer(serializer)
.build();
}
@Bean
@Primary
SimpleCommandBus commandBus(
AxonConfiguration axonConfiguration,
DuplicateCommandHandlerResolver duplicateCommandHandlerResolver,
TransactionManager txManager) {
SimpleCommandBus commandBus =
SimpleCommandBus.builder()
.transactionManager(txManager)
.duplicateCommandHandlerResolver(duplicateCommandHandlerResolver)
.messageMonitor(
axonConfiguration.messageMonitor(CommandBus.class, "commandBus"))
.build();
commandBus.registerHandlerInterceptor(
new CorrelationDataInterceptor(axonConfiguration.correlationDataProviders()));
return commandBus;
}
private Map<String, String> jpaProperties() {
Map<String, String> props = new HashMap<>();
props.put("hibernate.hbm2ddl.auto", "none");
props.put("hibernate.show_sql", "true");
props.put("hibernate.format_sql", "true");
props.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQL9Dialect");
props.put("hibernate.database_platform", "org.hibernate.dialect.PostgreSQL9Dialect");
return props;
}
(On version 4.2.1 of axon, with spring boot 2.5.7 this works fine: token_entry is the physical table name, and Axon is able to resolve it and move forward.) – Update: does not work fine, just does not stop the application from starting.
Upgrading Axon to 4.5.7 and Spring Boot to 2.6.2 results in getting the error that the table “tokenentry” is not found.
(Was there a significant change done to Axon? Or is Spring Boot to blame?) ----
Update: while the error is different, on the old versions I have a similar problem:
2022-02-08 20:05:37,573 - WARN - [EventProcessor[com.example.test]-0] - [TrackingEventProcessor.java:1208] - [::] - Fetch Segments for Processor 'com.example.test.projection' failed: Could not load segments for processor [com.example.test.projection]. Preparing for retry in 60s
While debugging, I get the same error in fetchSegments#JdbcTokenStore.java
- the tokenentry table does not exist.
How can I force axon to use a different physical naming strategy?
Thanks