Axonframework: Injecting `ApplicationContext` causes application to crash

Created on 1 Dec 2020  路  6Comments  路  Source: AxonFramework/AxonFramework

Basic information

  • Axon Framework version: 4.4.5
  • Spring Boot version: 2.4.0
  • JDK version: JDK14 w/ JDK11 compatibility
  • Complete executable reproducer if available (e.g. GitHub Repo): DemoAggregate

Injecting ApplicationContext into an aggregate handler causes the application to crash. Injecting other core classes such as AutowireCapableBeanFactory also causes the application to crash. Digging in, it appears that this line is returning an empty array. Perhaps this is being caused by the order in which beans are registered?

Steps to reproduce

  1. Execute application

Expected behaviour

Application should launch.

Actual behaviour

Application initialization throws UnsupportedHandlerException and crashes.

Would Resolved Bug

All 6 comments

Not to be a bother here @jnfeinstein, but why do you need core Spring beans into your Aggregate? The parameter resolution is intended to provide message specific parameters, as well as domain services you might need to operate on a command/message. Again, not to be offensive, but I'd be hard pressed to hear you need the entire ApplicationContext to make sure a command can be executed successfully. If this is the case though, please let me know so that we can see what we can do in this perspective.

@smcvb I expected no less from the AxonIQ team. 馃槃

data class ExecuteMigrationCommand(
    val klass: KClass<out Migration>
) {
    @TargetAggregateIdentifier
    val identifier = Migrations.IDENTIFIER
}

@CommandHandler
@CreationPolicy(AggregateCreationPolicy.CREATE_IF_MISSING)
fun handle(
    command: ExecuteMigrationCommand,
    applicationContext: ApplicationContext
): Boolean {
    if (isExecuted(command.klass)) return false
    applicationContext.build(command.klass).migrate()
    apply(MigrationExecutedEvent(command.klass.name()))
    return true
}

private fun ApplicationContext.build(klass: KClass<out Migration>): Migration {
    return autowireCapableBeanFactory.autowire(
        klass.java,
        AutowireCapableBeanFactory.AUTOWIRE_NO,
        false
    ) as Migration
}

What I have created is an event-sourced system for migrations a.k.a. single-use scripts. The aggregate provides a natural execution context for migrations as it is fully audited and locked in a clustered environment. The command contains the migration class to be executed, which is then autowired by the application context. One could in theory provide the migration object on the command itself, but I do not believe or trust that all potential arguments could necessarily be safely serialized and deserialized.

An alternative that I tried was to inject the repository for this aggregate into the migration executor to lock the aggregate, but this approach failed due to the lack of UnitOfWork.

@smcvb in addition, the documentation reads If the application is run in a Spring environment, any Spring Bean can be resolved., which led me to believe that this was a bug.

Fair point. If the documentation states this, you would indeed assume you can wire anything. I've put the issue under discussion to check what the rest of the team thinks about such an approach. Stay tuned. In the mean time, I hope it's not blocking you to harshly.

I was able to side-step the issue by adding a parameter resolver bean specifically to resolve the ApplicationContext. Which proves that the beans are present and perhaps the method in which they are being retrieved is faulty or dated.

Ooooh, that's interesting! Good to hear you've found a workaround for now. Any chance you'd be up for providing a PR to resolve this from within Axon too? ;-)

Was this page helpful?
0 / 5 - 0 ratings