Quarkus core components - runtime library for the Cloud Native, Container First Java framework
—
The Build-Time Processing capability provides the recorder system for build-time code generation and bytecode manipulation in Quarkus applications.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Recorder {
// Marks classes as build-time recorders for bytecode generation
}@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface StaticInit {
// Marks recorder methods for static initialization phase (build-time)
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RuntimeInit {
// Marks recorder methods for runtime initialization phase (startup)
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface StaticInitSafe {
// Marks classes as safe for static initialization
}@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.CONSTRUCTOR)
public @interface RecordableConstructor {
// Marks constructors as recordable for bytecode generation
}@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface QuarkusMain {
/**
* Unique name for the main method (optional).
* @return Main method name
*/
String name() default "";
}@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface BuildStep {
/**
* Only execute this build step if all of the given suppliers return true.
* @return Array of boolean suppliers for conditional execution
*/
Class<? extends BooleanSupplier>[] onlyIf() default {};
/**
* Only execute this build step if none of the given suppliers return true.
* @return Array of boolean suppliers for negative conditional execution
*/
Class<? extends BooleanSupplier>[] onlyIfNot() default {};
}// Base class for all build items
public abstract class BuildItem {
// All build items must extend this class
}
// Build item for single-valued items
public abstract class SimpleBuildItem extends BuildItem {
// Used for build items that have only one instance
}
// Build item for multi-valued items (consumed as Lists)
public abstract class MultiBuildItem extends BuildItem {
// Used for build items that can have multiple instances
}
// Build producer for creating build items
public interface BuildProducer<T extends BuildItem> {
void produce(T item);
}
// Build consumer for consuming build items
public interface Consumer<T> {
void accept(T t);
}@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Record {
/**
* Execution time for the recorded method.
* @return Execution time (STATIC_INIT or RUNTIME_INIT)
*/
ExecutionTime value();
}
// Execution time enum
public enum ExecutionTime {
STATIC_INIT, // Execute at build time
RUNTIME_INIT // Execute at runtime initialization
}import io.quarkus.runtime.annotations.Recorder;
import io.quarkus.runtime.annotations.StaticInit;
import io.quarkus.runtime.annotations.RuntimeInit;
@Recorder
public class MyServiceRecorder {
@StaticInit
public void configureStaticSettings() {
// This method is executed at build time
// Used for static configuration that doesn't change at runtime
System.setProperty("quarkus.my-service.static-config", "enabled");
// Register static resources
registerStaticResources();
}
@RuntimeInit
public MyServiceRuntimeConfig configureRuntimeSettings(MyServiceBuildConfig buildConfig) {
// This method is executed at runtime startup
// Uses build-time configuration to create runtime configuration
MyServiceRuntimeConfig runtimeConfig = new MyServiceRuntimeConfig();
runtimeConfig.setEndpoint(buildConfig.endpoint);
runtimeConfig.setTimeout(buildConfig.timeout);
// Initialize runtime components
initializeRuntimeComponents(runtimeConfig);
return runtimeConfig;
}
private void registerStaticResources() {
// Register resources that are known at build time
}
private void initializeRuntimeComponents(MyServiceRuntimeConfig config) {
// Initialize components that need runtime configuration
}
}import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigRoot;
import io.quarkus.runtime.annotations.ConfigPhase;
// Build-time configuration
@ConfigRoot(phase = ConfigPhase.BUILD_TIME)
public class MyServiceBuildConfig {
/**
* Service endpoint URL.
*/
@ConfigItem(defaultValue = "http://localhost:8080")
public String endpoint;
/**
* Connection timeout in seconds.
*/
@ConfigItem(defaultValue = "30")
public int timeout;
/**
* Enable debug mode.
*/
@ConfigItem(defaultValue = "false")
public boolean debug;
}
// Runtime configuration
@ConfigRoot(phase = ConfigPhase.RUNTIME_INIT)
public class MyServiceRuntimeConfig {
/**
* Runtime service endpoint.
*/
@ConfigItem
public String endpoint;
/**
* Runtime timeout.
*/
@ConfigItem
public int timeout;
// Setters for recorder
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
}import io.quarkus.runtime.annotations.Recorder;
import io.quarkus.runtime.annotations.StaticInit;
import io.quarkus.runtime.annotations.RuntimeInit;
import javax.sql.DataSource;
@Recorder
public class DatabaseRecorder {
@StaticInit
public void registerDriver(String driverClassName) {
// Register JDBC driver at build time
try {
Class.forName(driverClassName);
System.out.println("Registered JDBC driver: " + driverClassName);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Failed to register JDBC driver: " + driverClassName, e);
}
}
@RuntimeInit
public DataSource createDataSource(DatabaseConfig config) {
// Create DataSource at runtime with actual connection parameters
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setJdbcUrl(config.url);
hikariConfig.setUsername(config.username);
hikariConfig.setPassword(config.password);
hikariConfig.setMaximumPoolSize(config.maxPoolSize);
hikariConfig.setConnectionTimeout(config.connectionTimeout.toMillis());
DataSource dataSource = new HikariDataSource(hikariConfig);
// Register as CDI bean
Arc.container().instance(DataSource.class).get().setDataSource(dataSource);
return dataSource;
}
@RuntimeInit
public void validateConnection(DataSource dataSource) {
// Validate database connection at startup
try (Connection conn = dataSource.getConnection()) {
if (conn.isValid(5)) {
System.out.println("Database connection validated successfully");
} else {
throw new RuntimeException("Database connection validation failed");
}
} catch (SQLException e) {
throw new RuntimeException("Failed to validate database connection", e);
}
}
}import io.quarkus.runtime.annotations.Recorder;
import io.quarkus.runtime.annotations.StaticInit;
import io.quarkus.runtime.annotations.RuntimeInit;
import java.net.http.HttpClient;
import java.time.Duration;
@Recorder
public class HttpClientRecorder {
@StaticInit
public void configureHttpClientDefaults() {
// Set system properties for HTTP client at build time
System.setProperty("jdk.httpclient.allowRestrictedHeaders", "true");
System.setProperty("jdk.httpclient.keepalive.timeout", "30");
}
@RuntimeInit
public HttpClient createHttpClient(HttpClientConfig config) {
// Create configured HTTP client at runtime
HttpClient.Builder builder = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(config.connectTimeout))
.followRedirects(HttpClient.Redirect.NORMAL);
if (config.enableHttp2) {
builder.version(HttpClient.Version.HTTP_2);
}
if (config.enableCompression) {
// Enable compression if supported
}
HttpClient client = builder.build();
// Register as singleton
registerHttpClientSingleton(client);
return client;
}
private void registerHttpClientSingleton(HttpClient client) {
// Register HTTP client as CDI singleton
}
}import io.quarkus.runtime.annotations.Recorder;
import io.quarkus.runtime.annotations.StaticInit;
import io.quarkus.runtime.annotations.RuntimeInit;
import java.security.Security;
@Recorder
public class SecurityRecorder {
@StaticInit
public void registerSecurityProviders() {
// Register security providers at build time
Security.addProvider(new BouncyCastleProvider());
System.out.println("Registered BouncyCastle security provider");
}
@StaticInit
public void configureSecurityProperties() {
// Configure security properties at build time
Security.setProperty("crypto.policy", "unlimited");
Security.setProperty("securerandom.source", "file:/dev/urandom");
}
@RuntimeInit
public SecurityContext initializeSecurityContext(SecurityConfig config) {
// Initialize security context at runtime
SecurityContext context = new SecurityContext();
if (config.enableJwt) {
JwtProcessor jwtProcessor = createJwtProcessor(config);
context.setJwtProcessor(jwtProcessor);
}
if (config.enableOAuth) {
OAuthProvider oauthProvider = createOAuthProvider(config);
context.setOAuthProvider(oauthProvider);
}
return context;
}
private JwtProcessor createJwtProcessor(SecurityConfig config) {
// Create JWT processor with runtime configuration
return new JwtProcessor(config.jwtSecret, config.jwtExpiration);
}
private OAuthProvider createOAuthProvider(SecurityConfig config) {
// Create OAuth provider with runtime configuration
return new OAuthProvider(config.oauthClientId, config.oauthClientSecret);
}
}import io.quarkus.runtime.annotations.RecordableConstructor;
public class RecordableService {
private final String configuration;
private final int port;
// This constructor can be recorded and called during build-time processing
@RecordableConstructor
public RecordableService(String configuration, int port) {
this.configuration = configuration;
this.port = port;
}
// Regular constructor (not recordable)
public RecordableService() {
this("default", 8080);
}
public void start() {
System.out.println("Starting service with config: " + configuration + " on port: " + port);
}
}
// Recorder using the recordable constructor
@Recorder
public class ServiceRecorder {
@RuntimeInit
public RecordableService createService(ServiceConfig config) {
// This will generate bytecode that calls the recordable constructor
return new RecordableService(config.name, config.port);
}
}import io.quarkus.runtime.annotations.QuarkusMain;
import io.quarkus.runtime.QuarkusApplication;
@QuarkusMain(name = "web-server")
public class WebServerMain implements QuarkusApplication {
@Override
public int run(String... args) throws Exception {
System.out.println("Starting web server");
// Web server logic
return 0;
}
}
@QuarkusMain(name = "batch-job")
public class BatchJobMain implements QuarkusApplication {
@Override
public int run(String... args) throws Exception {
System.out.println("Running batch job");
// Batch processing logic
return 0;
}
}
@QuarkusMain(name = "data-migration")
public class DataMigrationMain implements QuarkusApplication {
@Override
public int run(String... args) throws Exception {
System.out.println("Running data migration");
// Migration logic
return 0;
}
}Usage:
# Run web server (default)
java -jar myapp.jar
# Run specific main method
java -jar myapp.jar -Dquarkus.main.name=batch-job
java -jar myapp.jar -Dquarkus.main.name=data-migrationimport io.quarkus.runtime.annotations.StaticInitSafe;
@StaticInitSafe
public class ConfigurationUtils {
// This class is safe to initialize during static init phase
public static final String DEFAULT_CONFIG = "default-config.properties";
public static final int MAX_RETRIES = 3;
static {
// Static initialization block is safe to run at build time
System.out.println("ConfigurationUtils initialized");
}
public static String loadConfiguration(String filename) {
// This method can be safely called during static initialization
try (InputStream is = ConfigurationUtils.class.getResourceAsStream(filename)) {
return new String(is.readAllBytes(), StandardCharsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException("Failed to load configuration", e);
}
}
}
// Recorder using static init safe class
@Recorder
public class ConfigRecorder {
@StaticInit
public void loadStaticConfiguration() {
// Safe to call static methods from @StaticInitSafe classes
String config = ConfigurationUtils.loadConfiguration(ConfigurationUtils.DEFAULT_CONFIG);
System.setProperty("app.static.config", config);
}
}import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.builditem.FeatureBuildItem;
public class MyServiceProcessor {
private static final String FEATURE = "my-service";
@BuildStep
FeatureBuildItem feature() {
return new FeatureBuildItem(FEATURE);
}
@BuildStep
@Record(ExecutionTime.STATIC_INIT)
void configureStaticInit(MyServiceRecorder recorder, MyServiceBuildConfig config) {
// Call recorder method during static initialization
recorder.configureStaticSettings();
if (config.debug) {
recorder.enableDebugMode();
}
}
@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
MyServiceRuntimeConfigBuildItem configureRuntimeInit(
MyServiceRecorder recorder,
MyServiceBuildConfig buildConfig) {
// Call recorder method during runtime initialization
MyServiceRuntimeConfig runtimeConfig = recorder.configureRuntimeSettings(buildConfig);
return new MyServiceRuntimeConfigBuildItem(runtimeConfig);
}
}@StaticInit for configuration that doesn't change at runtime@RuntimeInit for components that need runtime configuration@StaticInitSafe when they can be safely initialized at build time@RecordableConstructor for objects created in recorders@RuntimeInit methods to improve startup timeInstall with Tessl CLI
npx tessl i tessl/maven-io-quarkus--quarkus-core