Core deployment-time APIs and infrastructure for building Quarkus applications with native image support and development-time features
—
Quarkus development mode provides hot reloading, live coding, development services, and enhanced debugging capabilities to create a seamless developer experience. The development mode infrastructure handles file watching, incremental compilation, and runtime updates without requiring application restarts.
// Development mode context and processing
import io.quarkus.deployment.dev.DevModeContext;
import io.quarkus.deployment.dev.RuntimeUpdatesProcessor;
import io.quarkus.deployment.dev.CompilationProvider;
import io.quarkus.deployment.dev.DevModeListener;
import io.quarkus.deployment.dev.ClassScanResult;
// File system watching and hot deployment
import io.quarkus.deployment.dev.filesystem.StaticFileSystemWatcher;
import io.quarkus.deployment.dev.filesystem.ReloadableFileSystemWatcher;
import io.quarkus.deployment.dev.HotDeploymentWatchedFileBuildStep;
// Development services
import io.quarkus.deployment.dev.devservices.DevServicesBuildItem;
import io.quarkus.deployment.dev.devservices.DevServicesConfig;
import io.quarkus.deployment.dev.devservices.DevServicesResultBuildItem;
// Remote development mode
import io.quarkus.deployment.dev.remote.RemoteDevClient;
import io.quarkus.deployment.dev.remote.RemoteDevConfig;
// Testing integration
import io.quarkus.deployment.dev.testing.TestSupport;
import io.quarkus.deployment.dev.testing.TestConfig;
import io.quarkus.deployment.dev.testing.TestRunResults;
// Console and IDE integration
import io.quarkus.deployment.console.ConsoleCommand;
import io.quarkus.deployment.console.ConsoleStateManager;
import io.quarkus.deployment.ide.EffectiveIdeBuildItem;
import io.quarkus.deployment.ide.IdeConfig;Central processor for handling runtime updates and hot reloading.
class RuntimeUpdatesProcessor {
/**
* Singleton instance for runtime updates processing
*/
static RuntimeUpdatesProcessor INSTANCE;
/**
* Scans for changes and triggers updates if necessary
*/
boolean doScan(boolean userInitiated, boolean forceRestart);
/**
* Adds a hot replacement setup handler
*/
void addHotReplacementSetup(HotReplacementSetup service);
/**
* Checks for changed classes since last scan
*/
ClassScanResult checkForChangedClasses();
/**
* Gets the current application context
*/
DevModeContext getContext();
/**
* Restarts the application completely
*/
void restart(boolean forceRestart);
/**
* Pauses file watching
*/
void pause();
/**
* Resumes file watching
*/
void resume();
/**
* Checks if currently in dev mode
*/
static boolean isDevMode();
}Context object containing development mode configuration and module information.
class DevModeContext {
/**
* Gets all modules in the development context
*/
List<ModuleInfo> getAllModules();
/**
* Gets the main application module
*/
ModuleInfo getApplicationRoot();
/**
* Gets command line arguments
*/
String[] getArgs();
/**
* Gets the launch mode
*/
LaunchMode getLaunchMode();
/**
* Gets the project directory
*/
Path getProjectDir();
/**
* Checks if this is a test
*/
boolean isTest();
/**
* Checks if local project dependencies should be disabled
*/
boolean isLocalProjectDiscovery();
/**
* Information about a module in the development context
*/
static class ModuleInfo {
String getArtifactKey();
String getName();
Path getProjectDirectory();
Path getSourceDirectory();
Path getClassesDirectory();
Path getResourceDirectory();
Set<String> getSourcePaths();
CompileClasspathManager getCompileClasspathManager();
}
}Usage Examples:
@BuildStep(onlyIf = IsDevelopment.class)
void setupHotReload(DevModeContext context,
BuildProducer<HotDeploymentWatchedFileBuildItem> watchedFiles) {
// Watch configuration files for changes
context.getAllModules().forEach(module -> {
Path resourceDir = module.getResourceDirectory();
if (Files.exists(resourceDir.resolve("application.properties"))) {
watchedFiles.produce(new HotDeploymentWatchedFileBuildItem(
resourceDir.resolve("application.properties").toString(),
true
));
}
});
}
@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
void initializeDevMode(DevModeRecorder recorder,
LaunchModeBuildItem launchMode,
DevModeContext context) {
if (launchMode.getLaunchMode() == LaunchMode.DEVELOPMENT) {
recorder.setupDevMode(context.getArgs(), context.getProjectDir());
}
}class StaticFileSystemWatcher {
/**
* Watches static resources for changes
*/
StaticFileSystemWatcher(Path watchedPath,
Set<String> includedExtensions,
Consumer<Set<String>> changeCallback);
/**
* Starts watching for file changes
*/
void start();
/**
* Stops file watching
*/
void stop();
}
class ReloadableFileSystemWatcher {
/**
* Watches source files for changes requiring compilation
*/
void watchPath(Path path, String... extensions);
/**
* Gets changed files since last check
*/
Set<Path> getChangedPaths();
/**
* Resets the changed files tracking
*/
void reset();
}class HotDeploymentWatchedFileBuildItem extends MultiBuildItem {
/**
* Creates a watched file item
*/
HotDeploymentWatchedFileBuildItem(String location);
/**
* Creates a watched file item with restart behavior
*/
HotDeploymentWatchedFileBuildItem(String location, boolean restartNeeded);
String getLocation();
boolean isRestartNeeded();
}
class ClassScanResult {
/**
* Classes that have been added
*/
Set<String> getAddedClasses();
/**
* Classes that have been modified
*/
Set<String> getChangedClasses();
/**
* Classes that have been deleted
*/
Set<String> getDeletedClasses();
/**
* Whether changes require a restart
*/
boolean isRestartNeeded();
}Usage Examples:
@BuildStep(onlyIf = IsDevelopment.class)
void setupFileWatching(ApplicationArchivesBuildItem archives,
BuildProducer<HotDeploymentWatchedFileBuildItem> watchedFiles) {
// Watch templates for changes
watchedFiles.produce(new HotDeploymentWatchedFileBuildItem("templates/", false));
// Watch configuration that requires restart
watchedFiles.produce(new HotDeploymentWatchedFileBuildItem("application.yaml", true));
// Watch web resources
watchedFiles.produce(new HotDeploymentWatchedFileBuildItem("META-INF/resources/", false));
}
public class HotReloadSetup implements HotReplacementSetup {
@Override
public void setupHotDeployment(HotReplacementContext context) {
context.consumeNoRestartChanges(changedResources -> {
// Handle resource changes that don't require restart
for (String resource : changedResources) {
if (resource.endsWith(".html") || resource.endsWith(".css")) {
// Refresh web resources
refreshWebResource(resource);
}
}
});
}
}Service provider interface for handling compilation of different file types in development mode.
interface CompilationProvider {
/**
* File extensions this provider can handle
*/
Set<String> handledExtensions();
/**
* Compiles the specified files
*/
void compile(Set<File> filesToCompile, Context context);
/**
* Compilation context providing build information
*/
interface Context {
/**
* Source directory for compilation
*/
Path getSourceDirectory();
/**
* Output directory for compiled classes
*/
Path getOutputDirectory();
/**
* Classpath for compilation
*/
ClassLoader getClasspathClassLoader();
/**
* Gets the project directory
*/
Path getProjectDirectory();
/**
* Gets compiler arguments
*/
List<String> getCompilerOptions();
}
}Implementation Example:
public class KotlinCompilationProvider implements CompilationProvider {
@Override
public Set<String> handledExtensions() {
return Set.of(".kt", ".kts");
}
@Override
public void compile(Set<File> filesToCompile, Context context) {
if (filesToCompile.isEmpty()) {
return;
}
List<String> args = new ArrayList<>();
args.add("-cp");
args.add(buildClasspath(context));
args.add("-d");
args.add(context.getOutputDirectory().toString());
// Add Kotlin-specific options
args.addAll(context.getCompilerOptions());
// Add source files
filesToCompile.forEach(file -> args.add(file.getAbsolutePath()));
// Execute Kotlin compiler
KotlinCompiler.exec(args.toArray(new String[0]));
}
}
@BuildStep
void registerKotlinCompiler(BuildProducer<CompilationProviderBuildItem> providers) {
providers.produce(new CompilationProviderBuildItem(KotlinCompilationProvider.class));
}Development services automatically start external services like databases, message brokers, etc., during development.
class DevServicesConfig {
/**
* Whether dev services are enabled globally
*/
@WithDefault("true")
boolean enabled;
/**
* Timeout for starting services
*/
@WithDefault("60s")
Duration timeout;
/**
* Whether to share services between applications
*/
@WithDefault("true")
boolean shared;
}
class DevServicesBuildItem extends MultiBuildItem {
/**
* Creates a dev service build item
*/
DevServicesBuildItem(String name,
String containerImage,
Map<String, String> containerProperties,
Map<String, Object> configProperties);
String getName();
String getContainerImage();
Map<String, String> getContainerProperties();
Map<String, Object> getConfigProperties();
}
class DevServicesResultBuildItem extends SimpleBuildItem {
Map<String, String> getConfig();
Closeable getCloseable();
}Usage Examples:
@BuildStep(onlyIf = { IsDevelopment.class, DevServicesEnabled.class })
DevServicesBuildItem startDatabase(LaunchModeBuildItem launchMode,
DatabaseConfig config) {
if (config.devservices.enabled && config.url.isEmpty()) {
Map<String, String> containerProps = Map.of(
"POSTGRES_DB", "devdb",
"POSTGRES_USER", "dev",
"POSTGRES_PASSWORD", "dev"
);
Map<String, Object> configProps = Map.of(
"quarkus.datasource.jdbc.url", "jdbc:postgresql://localhost:5432/devdb",
"quarkus.datasource.username", "dev",
"quarkus.datasource.password", "dev"
);
return new DevServicesBuildItem(
"postgresql",
"postgres:13",
containerProps,
configProps
);
}
return null;
}
@BuildStep
DevServicesResultBuildItem configureDevServices(List<DevServicesBuildItem> devServices,
LaunchModeBuildItem launchMode) {
Map<String, String> combinedConfig = new HashMap<>();
List<Closeable> closeables = new ArrayList<>();
for (DevServicesBuildItem devService : devServices) {
// Start container and collect configuration
ContainerInfo container = startContainer(devService);
combinedConfig.putAll(container.getConfig());
closeables.add(container::stop);
}
return new DevServicesResultBuildItem(combinedConfig, () -> {
for (Closeable closeable : closeables) {
closeable.close();
}
});
}interface ConsoleCommand {
/**
* Command name for console input
*/
String getCommand();
/**
* Description of the command
*/
String getDescription();
/**
* Executes the command
*/
void execute(String[] args, ConsoleContext context);
/**
* Whether command is available in current mode
*/
default boolean isAvailable() { return true; }
}
class ConsoleStateManager {
/**
* Registers a console command
*/
void addCommand(ConsoleCommand command);
/**
* Processes console input
*/
void handleInput(String input);
/**
* Sets up console reading thread
*/
void setupConsole();
}Implementation Example:
public class TestCommand implements ConsoleCommand {
@Override
public String getCommand() {
return "t";
}
@Override
public String getDescription() {
return "Run tests";
}
@Override
public void execute(String[] args, ConsoleContext context) {
TestSupport.runTests();
}
@Override
public boolean isAvailable() {
return LaunchMode.current() == LaunchMode.DEVELOPMENT;
}
}
@BuildStep(onlyIf = IsDevelopment.class)
void registerConsoleCommands(BuildProducer<ConsoleCommandBuildItem> commands) {
commands.produce(new ConsoleCommandBuildItem(new TestCommand()));
commands.produce(new ConsoleCommandBuildItem(new RestartCommand()));
commands.produce(new ConsoleCommandBuildItem(new HelpCommand()));
}class RemoteDevClient {
/**
* Connects to remote development server
*/
static RemoteDevClient connect(RemoteDevConfig config);
/**
* Sends local changes to remote server
*/
void sendChanges(Set<Path> changedFiles);
/**
* Receives updates from remote server
*/
void receiveUpdates();
/**
* Disconnects from remote server
*/
void disconnect();
}
class RemoteDevConfig {
/**
* Remote server URL
*/
String url;
/**
* Authentication token
*/
Optional<String> password;
/**
* Connection timeout
*/
@WithDefault("30s")
Duration connectTimeout;
}Usage Example:
@BuildStep(onlyIf = { IsDevelopment.class, RemoteDevEnabled.class })
@Record(ExecutionTime.RUNTIME_INIT)
void setupRemoteDev(RemoteDevRecorder recorder,
RemoteDevConfig config,
DevModeContext context) {
recorder.setupRemoteDevClient(
config.url,
config.password.orElse(null),
context.getProjectDir()
);
}class TestSupport {
/**
* Runs all tests in development mode
*/
static TestRunResults runTests();
/**
* Runs specific test classes
*/
static TestRunResults runTests(Set<String> testClasses);
/**
* Checks if tests are currently running
*/
static boolean isRunning();
/**
* Gets the last test results
*/
static Optional<TestRunResults> getLastResults();
}
class TestRunResults {
long getTestsRun();
long getTestsSkipped();
long getTestsFailed();
Duration getDuration();
List<TestFailure> getFailures();
}
class TestConfig {
/**
* Whether to run tests automatically on changes
*/
@WithDefault("false")
boolean continuous;
/**
* Test framework to use
*/
@WithDefault("junit5")
TestFramework framework;
/**
* Include patterns for test discovery
*/
List<String> includePattern;
/**
* Exclude patterns for test discovery
*/
List<String> excludePattern;
}Usage Example:
@BuildStep(onlyIf = IsDevelopment.class)
@Record(ExecutionTime.RUNTIME_INIT)
void setupTestSupport(TestRecorder recorder,
TestConfig config,
ApplicationIndexBuildItem index) {
List<String> testClasses = index.getIndex()
.getAllKnownImplementors(DotName.createSimple("org.junit.jupiter.api.Test"))
.stream()
.map(classInfo -> classInfo.name().toString())
.collect(Collectors.toList());
recorder.setupTestSupport(testClasses, config.continuous);
}class IdeConfig {
/**
* Target IDE for integration
*/
Optional<IdeType> target;
/**
* Whether to open URLs in IDE
*/
@WithDefault("true")
boolean openUrls;
enum IdeType {
IDEA, ECLIPSE, VSCODE, NETBEANS
}
}
class EffectiveIdeBuildItem extends SimpleBuildItem {
Optional<IdeType> getIdeType();
boolean isIdePresent();
/**
* Opens a file in the IDE at specific line
*/
void openFile(Path file, int line);
/**
* Opens a URL in the IDE
*/
void openUrl(String url);
}Usage Example:
@BuildStep
EffectiveIdeBuildItem detectIde(IdeConfig config) {
// Detect IDE based on environment variables, system properties, etc.
Optional<IdeType> detectedIde = detectCurrentIde();
IdeType effectiveIde = config.target.orElse(detectedIde.orElse(null));
return new EffectiveIdeBuildItem(Optional.ofNullable(effectiveIde));
}
@BuildStep(onlyIf = IsDevelopment.class)
void setupIdeIntegration(EffectiveIdeBuildItem ide,
BuildProducer<ConsoleCommandBuildItem> commands) {
if (ide.isIdePresent()) {
commands.produce(new ConsoleCommandBuildItem(new OpenInIdeCommand(ide)));
}
}Install with Tessl CLI
npx tessl i tessl/maven-io--quarkus--quarkus-core-deployment