Class SharedAxonServerContainer

java.lang.Object
io.axoniq.framework.testcontainer.SharedAxonServerContainer

public final class SharedAxonServerContainer extends Object
Holds the single canonical AxonServerContainer configuration meant to be shared by every IT test suite in the build, instead of each test class declaring its own.

Testcontainers' reuse feature (a container-level GenericContainer.withReuse(boolean) combined with testcontainers.reuse.enable=true in ~/.testcontainers.properties) attaches to an already-running container instead of starting a new one, but only when the requested configuration hashes identically to one already running. Every test class that used to hand-roll its own AxonServerContainer therefore started a fresh container, even within the same build. Depending on INSTANCE here instead, rather than constructing a separate AxonServerContainer, is what lets those requests collapse onto the same running container.

This class lives in this module specifically because it is low enough in the dependency graph for every consumer -- connector/axon-server-connector, dependency-injection/spring/spring-boot-autoconfigure, and all integrationtests/* modules -- to reach it directly.

Since:
5.4.0
Author:
John Hendrikx
  • Field Details

    • LICENSE_ENV_VARIABLE

      public static final String LICENSE_ENV_VARIABLE
      Name of the environment variable holding the content of the Axoniq test license. Takes precedence over LICENSE_RESOURCE when set. Because every module and forked JVM of a build sees the same environment, this makes all of them agree on whether INSTANCE is licensed.
      See Also:
    • LICENSE_RESOURCE

      public static final String LICENSE_RESOURCE
      Classpath resource name of the Axon Server Enterprise test license, used when LICENSE_ENV_VARIABLE is not set. Only present (and thus only mounted, see INSTANCE) in modules whose test classpath includes it; absent elsewhere. Tests that don't need multi-context features work identically either way.
      See Also:
    • INSTANCE

      public static final AxonServerContainer INSTANCE
      The shared container instance. Byte-identical construction is the entire point: it is what lets Testcontainers' reuse hash match across every module and JVM fork that references it.
  • Method Details

    • ensureStarted

      public static void ensureStarted()
      Starts INSTANCE if it isn't already running.

      GenericContainer.start() is idempotent on its own -- Testcontainers makes it a no-op when the container is already running in this JVM -- so this is a thin, explicitly-named entry point for callers rather than a correctness requirement.

      synchronized alone only guards within one JVM, not across forked processes, and Testcontainers' own findContainerForReuse() has no locking of its own (confirmed via its source: a // TODO locking comment). Two Surefire/Failsafe forks can therefore both decide INSTANCE isn't running yet and both end up creating their own container instead of one attaching to the other's -- confirmed directly: from a clean Docker state, two forks calling this method concurrently produced two separate containers on two different ports, even though both compute the identical reuse hash. A FileLock serializes the whole check-and-start sequence across processes, so the second caller sees the first one's container already running (and matching on hash) by the time it gets to check. Kept synchronized too: a second FileLock acquisition attempt from another thread in the same JVM throws OverlappingFileLockException rather than waiting, so cross-thread safety within this JVM still needs the intrinsic lock.

    • licenseExists

      public static boolean licenseExists()
      Checks whether an Axoniq license is available, either through the LICENSE_ENV_VARIABLE environment variable or as the LICENSE_RESOURCE on this JVM's classpath.
      Returns:
      true if a non-empty license is available, false otherwise