AF5: @InjectEntity not working on static handler (Migration of CREATE_IF_MISSING)

Hi
the AF5 migration guide says that you can use @InjectEntity in the static entity creator method:

CreationPolicy.CREATE_IF_MISSING

If you need “only create if missing” logic, you can implement it within the static handler by checking for the existence of an optional state parameter.

@CommandHandler
public static void handle(IssueGiftCard cmd,
                          EventAppender eventAppender,
                          @InjectEntity @Nullable GiftCard giftCard) {
    if (giftCard != null) {
        throw new IllegalStateException("GiftCard already exists");
    }
    eventAppender.append(new GiftCardIssued(cmd.cardId(), cmd.amount()));
}

I don’t know if this is “just” a bug in the migration docs or if this is a bug in Axon.

I get an EntityAlreadyExistsForCreationalCommandHandlerException before the static handle method is called again with the entity injected.

Simple example reproducing this using the CourseCreation example:

@Validated
@EventSourced(tagKey = FacultyTags.COURSE_ID, idType = CourseId.class)
@Slf4j
public class CourseCreation {

  private boolean created = false;
  private CourseId id;
  private int capacity;

  @CommandHandler
  public static void create(
      @Valid CreateCourse command,
      EventAppender appender,
      @InjectEntity @Nullable CourseCreation entity) {
    if (entity == null) {
      appender.append(
          new CourseCreated(
              command.courseId(),
              command.name(),
              command.capacity()
          )
      );
    } else {
      // do something different
    }
  }

  @EntityCreator
  public CourseCreation(CourseCreated courseCreated) {
    this.id = courseCreated.courseId();
    this.created = true;
    this.capacity = courseCreated.capacity();
  }
}

Following test fails:

    fixture.given()
        .events(new CourseCreated(courseId, "Introduction to Axon Framework", 30))
        .when()
        .command(new CreateCourse(courseId, "Introduction to Axon Framework", 30))
        .then()
        .success()
        ;

with

org.axonframework.modelling.entity.EntityAlreadyExistsForCreationalCommandHandlerException: Creational command handler for command [org.axonframework.examples.university.write.createcourse.CreateCourse#0.0.1] encountered an already existing entity: [org.axonframework.examples.university.write.createcourse.CourseCreation@6b68cb27]
	at org.axonframework.modelling.entity.ConcreteEntityMetamodel.handleInstance(ConcreteEntityMetamodel.java:147)
	at org.axonframework.modelling.entity.annotation.AnnotatedEntityMetamodel.handleInstance(AnnotatedEntityMetamodel.java:500)
	at org.axonframework.modelling.entity.EntityCommandHandlingComponent.lambda$handle$1(EntityCommandHandlingComponent.java:107)
	at java.base/java.util.concurrent.CompletableFuture.uniApplyNow(CompletableFuture.java:684)
	at java.base/java.util.concurrent.CompletableFuture.uniApplyStage(CompletableFuture.java:662)
	at java.base/java.util.concurrent.CompletableFuture.thenApply(CompletableFuture.java:2200)
	at org.axonframework.modelling.entity.EntityCommandHandlingComponent.handle(EntityCommandHandlingComponent.java:103)

Klaus

Not a 100% sure, as I am mostly triggering on the title, but I think you’re hitting `EntityNotFoundException` behavior around `@EntityCreator` usage for Entity-centric and Stateful-Handler-based instance command handling by smcvb · Pull Request #4609 · AxonIQ/AxonFramework · GitHub here, @klauss42. If you move that command handler outside of the entity/aggregate, I’d anticipate things to work as desired.

Steven, you are right. If the static command handler is outside of the aggregate class, the injected entity is set. Kind of confusing…

Klaus

Yes, totally agreed it’s confusing! As there’s a PR, you can be ascertained the issue will be tackled accordingly in due time. Sorry for the inconvenience for the time being!