Anvil
Integrations

Embedding Anvil

Start scenarios from a Java application with explicit runtime dependencies and engine ownership.

Embed Anvil when your application owns scenario startup and cleanup rather than delegating them to JUnit or Gradle's foreground runner. Use AnvilLauncher to create a ScenarioEngine and keep the engine and its contexts in try-with-resources.

Assemble the application classpath

Use aligned artifacts under me.whereareiam.anvil. For a Paper environment using the bundled capabilities and MCProtocol, the dependency fragment in your application build is:

val anvilVersion = providers.gradleProperty("anvilVersion").get()

dependencies {
	implementation("me.whereareiam.anvil:default:$anvilVersion")
	implementation("me.whereareiam.anvil:launcher:$anvilVersion")

	runtimeOnly("me.whereareiam.anvil:platform-bukkit-agent:$anvilVersion")
	runtimeOnly("me.whereareiam.anvil:platform-paper-provider:$anvilVersion")
	runtimeOnly("me.whereareiam.anvil:protocol-mcprotocol:$anvilVersion")
}

Set anvilVersion to your chosen Anvil version in gradle.properties and configure the repositories from installation. Apply your normal Java/application build plugins. Add the provider and matching agent artifacts for additional platforms. Use the Gradle plugin reference for the unit-to-artifact mapping.

The launcher supplies engine composition and execution implementations. It does not select a platform, protocol library, or capability set for your application. Preserve service descriptors if you assemble another shaded JAR around these dependencies.

Start a supplied scenario

Put this helper at src/main/java/EmbeddedScenario.java in your application. Pass an AnvilScenario containing an entrypoint and any desired processes. If its workspace uses AssetSource.artifact("plugin-under-test"), the helper registers the supplied plugin JAR under that name:

import me.whereareiam.anvil.api.model.EngineOptions;
import me.whereareiam.anvil.api.model.process.lifecycle.ProcessTimeouts;
import me.whereareiam.anvil.api.model.scenario.AnvilScenario;
import me.whereareiam.anvil.api.scenario.ScenarioContext;
import me.whereareiam.anvil.api.scenario.ScenarioEngine;
import me.whereareiam.anvil.launcher.AnvilLauncher;

import java.nio.file.Path;
import java.time.Duration;

public final class EmbeddedScenario {
	public static void run(AnvilScenario scenario, Path pluginJar) {
		var options = EngineOptions.builder()
				.eulaAccepted(true)
				.protocolLibrary("mcprotocol")
				.workDirectory(Path.of("build", "anvil"))
				.keepFailedWorkspaces(true)
				.processTimeouts(ProcessTimeouts.builder().shutdown(Duration.ofSeconds(30)).build())
				.artifact("plugin-under-test", pluginJar)
				.build();

		try (ScenarioEngine engine = AnvilLauncher.create(options);
		     ScenarioContext context = engine.start(scenario)) {
			var entrypoint = context.processes().get(scenario.getEntrypoint());
			System.out.println("Ready at " + entrypoint.address());
			// Perform application actions and assertions while the context is open.
		}
	}
}

Call EmbeddedScenario.run(scenario, pluginJar) from your application with the scenario and an existing plugin JAR. The helper returns after closing the environment. To interact with it, add the actions inside its open context.

start() returns after processes and agents are ready and the setup hook has completed. Closing a context releases its players and processes. Closing the returned engine also releases remaining contexts, the protocol libraries it created, and artifact acquisition resources. Unexpected exceptions should retain their original cause; cleanup can add suppressed failures.

Use context.finish(false) when your application catches a failed journey and needs to report that outcome to cleanup policy. The default close() reports normal completion. To install global diagnostics or per-scenario attachments, use AnvilLauncher.builder() and add an engine extension.

Prepare before starting individual processes

When your application chooses which components to start, use engine.prepare(scenario) to obtain a ScenarioContext. The launcher prepares the complete topology, routes, and process inputs while leaving JVMs stopped. Call context.processes().start(name) for an individual component and context.start() for the remaining processes plus global extensions and scenario setup. The individual-start example shows this sequence and its cleanup.

The context owns unstarted resources too. Closing either the context or its engine releases them. Every assembly supplies this lifecycle through ScenarioFactory.create(...), returning the context before process startup. engine.start(...) is the convenience that prepares a context and completes its start() before handing it to the caller.

Configure other entry points

EngineProperties.fromSystemProperties() or EngineProperties.from(properties) decodes the JVM property contract into EngineOptions. Passing explicit options to the launcher uses those options directly.

For a terminal application, add me.whereareiam.anvil:tooling-runner at the matching version. Supply a Supplier<ScenarioEngine> that creates an engine owned by the runner, together with the application's input reader and output writer:

import me.whereareiam.anvil.runner.AnvilRunner;

// input is a Reader, output is a PrintWriter, and engineFactory creates a ScenarioEngine.
new AnvilRunner(input, output, engineFactory).run(arguments);

The runner acquires an engine only when a scenario starts and closes it when the session ends. Listing scenario definitions does not create an engine. The runner library does not include the default launcher; your application supplies its engine assembly. RunnerSession accepts the same engine-factory boundary for applications serving structured tooling instead of a terminal shell.

For a ready-to-run process using the default engine, add me.whereareiam.anvil:tooling-launcher. Its me.whereareiam.anvil.tooling.launcher.AnvilCli entry point binds standard streams and decodes engine properties. me.whereareiam.anvil.tooling.launcher.AnvilTooling serves the structured protocol. The Gradle integration selects this executable assembly automatically. The manual environment guide covers scenario selection and commands. For container execution, supply a configured DockerExecutionProvider to the launcher.

On this page