Core deployment-time APIs and infrastructure for building Quarkus applications with native image support and development-time features
—
Quarkus Core Deployment provides a comprehensive set of utility classes for common deployment-time operations including reflection, file system operations, service loading, artifact handling, container runtime detection, and various helper functions that support the build and deployment process.
// Reflection utilities
import io.quarkus.deployment.util.ReflectUtil;
import io.quarkus.deployment.util.ClassUtil;
import io.quarkus.deployment.util.DeploymentUtil;
// Service loading utilities
import io.quarkus.deployment.util.ServiceUtil;
// File system utilities
import io.quarkus.deployment.util.FileUtil;
import io.quarkus.deployment.util.IoUtil;
import io.quarkus.deployment.util.PathUtil;
// Artifact and dependency utilities
import io.quarkus.deployment.util.ArtifactInfoUtil;
import io.quarkus.deployment.util.ArtifactResultBuilder;
// Container runtime utilities
import io.quarkus.deployment.util.ContainerRuntimeUtil;
import io.quarkus.deployment.util.ProcessUtil;
// Hashing and validation utilities
import io.quarkus.deployment.util.HashUtil;
import io.quarkus.deployment.util.StringUtil;
// Bean and proxy utilities
import io.quarkus.deployment.bean.BeanArchiveIndex;
import io.quarkus.deployment.bean.JavaBeanUtil;
import io.quarkus.deployment.proxy.ProxyFactory;
// Type parsing and handling
import io.quarkus.deployment.types.ParsedType;
import io.quarkus.deployment.types.TypeParser;
// Code generation framework
import io.quarkus.deployment.CodeGenProvider;
import io.quarkus.deployment.CodeGenContext;
import io.quarkus.deployment.CodeGenerator;
import io.quarkus.bootstrap.prebuild.CodeGenException;Comprehensive reflection utilities for deployment-time introspection and manipulation.
class ReflectUtil {
/**
* Gets a method by name and parameter types
*/
static Method getMethod(Class<?> clazz, String name, Class<?>... parameterTypes)
throws NoSuchMethodException;
/**
* Gets a field by name
*/
static Field getField(Class<?> clazz, String name)
throws NoSuchFieldException;
/**
* Creates a new instance using default constructor
*/
static <T> T newInstance(Class<T> clazz);
/**
* Creates a new instance using constructor with parameters
*/
static <T> T newInstance(Class<T> clazz, Class<?>[] paramTypes, Object... args);
/**
* Invokes a method on an instance
*/
static Object invoke(Method method, Object instance, Object... args);
/**
* Invokes a static method
*/
static Object invokeStatic(Method method, Object... args);
/**
* Gets or sets a field value
*/
static Object getFieldValue(Field field, Object instance);
static void setFieldValue(Field field, Object instance, Object value);
/**
* Finds all methods with given annotation
*/
static List<Method> getMethodsWithAnnotation(Class<?> clazz,
Class<? extends Annotation> annotation);
/**
* Finds all fields with given annotation
*/
static List<Field> getFieldsWithAnnotation(Class<?> clazz,
Class<? extends Annotation> annotation);
/**
* Gets raw class from parameterized type
*/
static Class<?> getRawType(Type type);
/**
* Checks if class is assignable from type
*/
static boolean isAssignableFrom(Class<?> clazz, Type type);
}Usage Examples:
@BuildStep
void processReflectiveAccess(CombinedIndexBuildItem index,
BuildProducer<ReflectiveClassBuildItem> reflectiveClasses) {
// Find all classes with @Entity annotation
Collection<AnnotationInstance> entities = index.getIndex()
.getAnnotations(DotName.createSimple("javax.persistence.Entity"));
for (AnnotationInstance entity : entities) {
String className = entity.target().asClass().name().toString();
try {
Class<?> entityClass = Class.forName(className);
// Register class for reflection
reflectiveClasses.produce(ReflectiveClassBuildItem.builder(entityClass)
.constructors()
.methods()
.fields()
.build());
// Check for getter/setter methods
List<Method> getters = ReflectUtil.getMethodsWithAnnotation(
entityClass, Getter.class);
List<Method> setters = ReflectUtil.getMethodsWithAnnotation(
entityClass, Setter.class);
} catch (ClassNotFoundException e) {
// Handle missing class
}
}
}
@BuildStep
@Record(ExecutionTime.STATIC_INIT)
void setupReflection(MyRecorder recorder) {
// Use reflection to configure framework
recorder.configure(ReflectUtil.class);
}Utilities for loading and managing service implementations via ServiceLoader.
class ServiceUtil {
/**
* Loads all implementations of a service from classpath
*/
static <T> List<T> classPathServices(Class<T> serviceType, ClassLoader classLoader);
/**
* Loads first implementation of a service
*/
static <T> Optional<T> classPathService(Class<T> serviceType, ClassLoader classLoader);
/**
* Loads services with specific context class loader
*/
static <T> List<T> loadServices(Class<T> serviceType, ClassLoader classLoader);
/**
* Gets service implementation classes (not instances)
*/
static <T> List<Class<? extends T>> loadServiceClasses(Class<T> serviceType,
ClassLoader classLoader);
/**
* Checks if service implementation exists
*/
static boolean hasService(Class<?> serviceType, ClassLoader classLoader);
/**
* Creates service loader for given type
*/
static <T> ServiceLoader<T> load(Class<T> serviceType, ClassLoader classLoader);
}Usage Examples:
@BuildStep
void discoverCompilationProviders(BuildProducer<CompilationProviderBuildItem> providers) {
// Load all compilation providers from classpath
List<CompilationProvider> compilationProviders =
ServiceUtil.classPathServices(CompilationProvider.class,
Thread.currentThread().getContextClassLoader());
for (CompilationProvider provider : compilationProviders) {
providers.produce(new CompilationProviderBuildItem(provider.getClass()));
}
}
@BuildStep
void configureCodeGenerators(BuildProducer<CodeGenProviderBuildItem> codeGenProviders) {
// Check if specific code generator is available
if (ServiceUtil.hasService(GrpcCodeGenProvider.class,
Thread.currentThread().getContextClassLoader())) {
Optional<GrpcCodeGenProvider> provider = ServiceUtil.classPathService(
GrpcCodeGenProvider.class,
Thread.currentThread().getContextClassLoader());
if (provider.isPresent()) {
codeGenProviders.produce(new CodeGenProviderBuildItem(provider.get()));
}
}
}Comprehensive file system operations for deployment processing.
class FileUtil {
/**
* Copies a file from source to target
*/
static void copyFile(Path source, Path target) throws IOException;
/**
* Copies directory recursively
*/
static void copyDirectory(Path source, Path target) throws IOException;
/**
* Moves file or directory
*/
static void moveFile(Path source, Path target) throws IOException;
/**
* Deletes file or directory recursively
*/
static void deleteDirectory(Path directory) throws IOException;
static boolean deleteIfExists(Path path) throws IOException;
/**
* Creates directories if they don't exist
*/
static Path ensureDirectoryExists(Path directory) throws IOException;
/**
* Checks if directory is empty
*/
static boolean isDirectoryEmpty(Path directory) throws IOException;
/**
* Gets relative path between two paths
*/
static String relativize(Path base, Path child);
/**
* Reads file content as string
*/
static String readFileToString(Path file, StandardCharsets charset) throws IOException;
/**
* Writes string content to file
*/
static void writeStringToFile(Path file, String content, StandardCharsets charset)
throws IOException;
/**
* Lists files in directory with filter
*/
static List<Path> listFiles(Path directory, String... extensions) throws IOException;
/**
* Finds files recursively with pattern
*/
static List<Path> findFiles(Path directory, String pattern) throws IOException;
}Lower-level I/O utilities and stream operations.
class IoUtil {
/**
* Reads all bytes from input stream
*/
static byte[] readBytes(InputStream inputStream) throws IOException;
/**
* Copies input stream to output stream
*/
static long copy(InputStream input, OutputStream output) throws IOException;
/**
* Reads properties from input stream
*/
static Properties readProperties(InputStream inputStream) throws IOException;
/**
* Creates buffered reader for path
*/
static BufferedReader newBufferedReader(Path path, Charset charset) throws IOException;
/**
* Creates buffered writer for path
*/
static BufferedWriter newBufferedWriter(Path path, Charset charset) throws IOException;
/**
* Safely closes multiple closeables
*/
static void safeClose(Closeable... closeables);
}Usage Examples:
@BuildStep
void processConfigurationFiles(ApplicationArchivesBuildItem archives,
BuildProducer<ConfigurationBuildItem> config) throws IOException {
for (ApplicationArchive archive : archives.getAllApplicationArchives()) {
Path resourceDir = archive.apply(tree -> {
OpenPathTree.PathVisit visit = tree.visit("META-INF");
return visit != null ? visit.getPath() : null;
});
if (resourceDir != null) {
// Find all .properties files
List<Path> propFiles = FileUtil.findFiles(resourceDir, "*.properties");
for (Path propFile : propFiles) {
Properties props = new Properties();
try (InputStream is = Files.newInputStream(propFile)) {
props = IoUtil.readProperties(is);
}
// Process properties
processConfigProperties(props);
}
}
}
}
@BuildStep
void generateConfigurationFiles(ConfigurationBuildItem config,
BuildProducer<GeneratedResourceBuildItem> resources)
throws IOException {
Path tempDir = Files.createTempDirectory("quarkus-config");
try {
// Generate configuration file
Path configFile = tempDir.resolve("runtime-config.properties");
Properties runtimeProps = new Properties();
config.getPropertyNames().forEach(name -> {
runtimeProps.setProperty(name, config.getValue(name, String.class).orElse(""));
});
try (OutputStream os = Files.newOutputStream(configFile)) {
runtimeProps.store(os, "Generated runtime configuration");
}
// Read generated content
byte[] content = FileUtil.readFileToString(configFile, StandardCharsets.UTF_8)
.getBytes(StandardCharsets.UTF_8);
resources.produce(new GeneratedResourceBuildItem(
"META-INF/quarkus-runtime-config.properties", content));
} finally {
FileUtil.deleteDirectory(tempDir);
}
}Quarkus provides a comprehensive code generation framework that allows extensions to generate source code during the build process. This system enables build-time code generation from various input formats including protobuf, OpenAPI, GraphQL schemas, and custom domain-specific languages.
Interface for implementing custom code generators that run during the generate-sources phase.
interface CodeGenProvider {
/**
* Unique identifier for this code generator, corresponds to the directory
* name in generated-sources
*/
String providerId();
/**
* File extensions that this code generator can process
*/
String[] inputExtensions();
/**
* Name of the input directory relative to source roots
* For example: src/main/{inputDirectory} and src/test/{inputDirectory}
*/
String inputDirectory();
/**
* Optional absolute path override for input directory
* Called after init(), returning null uses default inputDirectory()
*/
default Path getInputDirectory() { return null; }
/**
* Initialize the provider with application model and build properties
*/
default void init(ApplicationModel model, Map<String, String> properties) {}
/**
* Trigger code generation process
* @param context Code generation context with paths and configuration
* @return true if any files were generated or modified
*/
boolean trigger(CodeGenContext context) throws CodeGenException;
/**
* Determine if code generator should run based on source directory and config
*/
default boolean shouldRun(Path sourceDir, Config config) {
return Files.isDirectory(sourceDir);
}
}Context object providing all necessary information for code generation.
class CodeGenContext {
/**
* Application model with dependency information
*/
ApplicationModel applicationModel();
/**
* Target directory for generated output
* Typically: target/generated-sources/{providerId}
*/
Path outDir();
/**
* Working directory (typically project build directory)
*/
Path workDir();
/**
* Input directory containing source files to process
* Typically: src/main/{inputDirectory} or src/test/{inputDirectory}
*/
Path inputDir();
/**
* Whether spawned processes should redirect I/O streams
*/
boolean shouldRedirectIO();
/**
* Build-time configuration
*/
Config config();
/**
* Whether generation is for tests (true) or main sources (false)
*/
boolean test();
}Static utility class for initializing and executing code generators.
class CodeGenerator {
/**
* Initialize and run all code generators for given parameters
*/
static void initAndRun(
QuarkusClassLoader classLoader,
PathCollection sourceParentDirs,
Path generatedSourcesDir,
Path buildDir,
Consumer<Path> sourceRegistrar,
ApplicationModel appModel,
Properties properties,
String launchMode,
boolean test
) throws CodeGenException;
/**
* Trigger a specific code generator
*/
static boolean trigger(
ClassLoader deploymentClassLoader,
CodeGenData data,
ApplicationModel appModel,
Config config,
boolean test
) throws CodeGenException;
/**
* Get build-time configuration for code generation
*/
static Config getConfig(
ApplicationModel appModel,
LaunchMode launchMode,
Properties buildSystemProps,
QuarkusClassLoader deploymentClassLoader
) throws CodeGenException;
}Usage Examples:
/**
* Example: Protocol Buffer code generator
*/
public class ProtobufCodeGenProvider implements CodeGenProvider {
@Override
public String providerId() {
return "protobuf";
}
@Override
public String[] inputExtensions() {
return new String[]{"proto"};
}
@Override
public String inputDirectory() {
return "proto";
}
@Override
public void init(ApplicationModel model, Map<String, String> properties) {
// Initialize protoc compiler path, output options, etc.
this.protocPath = properties.get("protoc.path");
this.javaOut = Boolean.parseBoolean(properties.get("protobuf.java.out"));
}
@Override
public boolean trigger(CodeGenContext context) throws CodeGenException {
Path inputDir = context.inputDir();
Path outputDir = context.outDir();
if (!Files.exists(inputDir)) {
return false;
}
boolean generated = false;
try (Stream<Path> protoFiles = Files.walk(inputDir)) {
List<Path> protoSources = protoFiles
.filter(p -> p.toString().endsWith(".proto"))
.collect(Collectors.toList());
if (!protoSources.isEmpty()) {
// Run protoc compiler
List<String> command = List.of(
protocPath,
"--java_out=" + outputDir,
"--proto_path=" + inputDir
);
command.addAll(protoSources.stream()
.map(Path::toString)
.collect(Collectors.toList()));
ProcessBuilder pb = new ProcessBuilder(command);
if (context.shouldRedirectIO()) {
pb.inheritIO();
}
Process process = pb.start();
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new CodeGenException("protoc compilation failed with exit code: " + exitCode);
}
generated = true;
}
} catch (IOException | InterruptedException e) {
throw new CodeGenException("Protocol buffer code generation failed", e);
}
return generated;
}
@Override
public boolean shouldRun(Path sourceDir, Config config) {
return Files.isDirectory(sourceDir) &&
config.getOptionalValue("quarkus.protobuf.enabled", Boolean.class).orElse(true);
}
}
/**
* Build step that configures code generation
*/
@BuildStep
void configureCodeGeneration(BuildProducer<CodeGenProviderBuildItem> codeGenProviders) {
codeGenProviders.produce(new CodeGenProviderBuildItem(ProtobufCodeGenProvider.class.getName()));
}
/**
* Integration with development mode
*/
@BuildStep
void setupDevModeCodeGeneration(LaunchModeBuildItem launchMode,
BuildProducer<HotDeploymentWatchedFileBuildItem> watchedFiles) {
if (launchMode.getLaunchMode() == LaunchMode.DEVELOPMENT) {
// Watch .proto files for changes
watchedFiles.produce(HotDeploymentWatchedFileBuildItem.builder()
.setLocationGlob("src/main/proto/**/*.proto")
.setRestartNeeded(true)
.build());
}
}Utilities for working with Maven artifacts and dependency information.
class ArtifactInfoUtil {
/**
* Parses artifact coordinates from string
*/
static ArtifactKey parseArtifactKey(String coords);
/**
* Creates artifact key from components
*/
static ArtifactKey createArtifactKey(String groupId, String artifactId,
String classifier, String type);
/**
* Gets artifact version from dependency
*/
static String getArtifactVersion(ResolvedDependency dependency);
/**
* Checks if artifact matches pattern
*/
static boolean matches(ArtifactKey artifact, String pattern);
/**
* Gets all transitive dependencies
*/
static Set<ResolvedDependency> getTransitiveDependencies(
ResolvedDependency rootDependency,
boolean includeOptional
);
/**
* Filters dependencies by scope
*/
static List<ResolvedDependency> filterByScope(
List<ResolvedDependency> dependencies,
String... scopes
);
/**
* Creates dependency tree string representation
*/
static String createDependencyTree(ApplicationModel applicationModel);
}Usage Examples:
@BuildStep
void analyzeDependencies(ApplicationArchivesBuildItem archives,
BuildProducer<DependencyAnalysisBuildItem> analysis) {
List<ResolvedDependency> runtimeDeps = new ArrayList<>();
for (ApplicationArchive archive : archives.getApplicationArchives()) {
ResolvedDependency dependency = archive.getResolvedDependency();
if (dependency != null) {
// Filter runtime dependencies
if (ArtifactInfoUtil.filterByScope(List.of(dependency), "compile", "runtime")
.contains(dependency)) {
runtimeDeps.add(dependency);
}
// Check for specific artifacts
if (ArtifactInfoUtil.matches(dependency.getKey(), "org.hibernate.*")) {
// Handle Hibernate dependency
handleHibernateDependency(dependency);
}
}
}
analysis.produce(new DependencyAnalysisBuildItem(runtimeDeps));
}Utilities for detecting and working with container runtimes like Docker and Podman.
class ContainerRuntimeUtil {
/**
* Detects available container runtime
*/
static Optional<ContainerRuntime> detectContainerRuntime();
/**
* Checks if container runtime is available
*/
static boolean isContainerRuntimeAvailable();
/**
* Gets container runtime executable path
*/
static Optional<String> getContainerRuntimeExecutable();
/**
* Executes container command
*/
static ProcessResult executeContainerCommand(List<String> command,
Path workingDirectory)
throws IOException;
/**
* Builds container image
*/
static ProcessResult buildImage(Path dockerfile,
String imageName,
Path context) throws IOException;
/**
* Runs container
*/
static ProcessResult runContainer(String imageName,
Map<String, String> env,
List<String> ports,
List<String> volumes) throws IOException;
enum ContainerRuntime {
DOCKER("docker"),
PODMAN("podman");
String getExecutableName();
List<String> getDefaultOptions();
}
}Utilities for executing external processes.
class ProcessUtil {
/**
* Executes process with arguments
*/
static ProcessResult execute(List<String> command,
Path workingDirectory,
Map<String, String> environment,
Duration timeout) throws IOException;
/**
* Executes process and returns result
*/
static ProcessResult executeAndWait(String... command) throws IOException;
/**
* Checks if executable is available in PATH
*/
static boolean isExecutableInPath(String executable);
/**
* Gets executable path
*/
static Optional<Path> findExecutable(String executable);
/**
* Process execution result
*/
static class ProcessResult {
int getExitCode();
String getStdout();
String getStderr();
boolean isSuccess();
Duration getDuration();
}
}Usage Examples:
@BuildStep(onlyIf = ContainerImageRequested.class)
void buildContainerImage(JarBuildItem jar,
ImageConfig imageConfig,
BuildProducer<ContainerImageBuildItem> containerImage)
throws IOException {
if (!ContainerRuntimeUtil.isContainerRuntimeAvailable()) {
throw new RuntimeException("No container runtime available for image building");
}
ContainerRuntime runtime = ContainerRuntimeUtil.detectContainerRuntime()
.orElseThrow(() -> new RuntimeException("Container runtime detection failed"));
// Generate Dockerfile
Path dockerfile = generateDockerfile(jar, imageConfig);
// Build image
String imageName = "quarkus-app:latest";
ProcessResult result = ContainerRuntimeUtil.buildImage(
dockerfile,
imageName,
jar.getPath().getParent()
);
if (!result.isSuccess()) {
throw new RuntimeException("Container image build failed: " + result.getStderr());
}
containerImage.produce(new ContainerImageBuildItem(imageName));
}Utilities for generating hashes and checksums.
class HashUtil {
/**
* Generates SHA-256 hash of file
*/
static String sha256(Path file) throws IOException;
/**
* Generates SHA-256 hash of bytes
*/
static String sha256(byte[] data);
/**
* Generates MD5 hash of file
*/
static String md5(Path file) throws IOException;
/**
* Generates hash using specified algorithm
*/
static String hash(Path file, String algorithm) throws IOException;
/**
* Verifies file against checksum
*/
static boolean verify(Path file, String expectedHash, String algorithm) throws IOException;
/**
* Generates hash of directory contents
*/
static String directoryHash(Path directory, String algorithm) throws IOException;
}String manipulation and validation utilities.
class StringUtil {
/**
* Converts string to kebab-case
*/
static String toKebabCase(String input);
/**
* Converts string to camelCase
*/
static String toCamelCase(String input);
/**
* Converts string to PascalCase
*/
static String toPascalCase(String input);
/**
* Checks if string is null or empty
*/
static boolean isNullOrEmpty(String str);
/**
* Checks if string is null, empty, or whitespace
*/
static boolean isNullOrBlank(String str);
/**
* Joins strings with delimiter
*/
static String join(String delimiter, String... parts);
static String join(String delimiter, Collection<String> parts);
/**
* Splits string and trims whitespace
*/
static List<String> splitAndTrim(String input, String delimiter);
/**
* Replaces placeholders in template
*/
static String substitute(String template, Map<String, String> replacements);
}Usage Examples:
@BuildStep
void generateHashManifest(JarBuildItem jar,
BuildProducer<GeneratedResourceBuildItem> resources) throws IOException {
// Generate hash of JAR file
String jarHash = HashUtil.sha256(jar.getPath());
// Generate hash of dependencies
Map<String, String> dependencyHashes = new HashMap<>();
if (jar.getLibraryDir().isPresent()) {
List<Path> libFiles = FileUtil.listFiles(jar.getLibraryDir().get(), "jar");
for (Path libFile : libFiles) {
String hash = HashUtil.sha256(libFile);
dependencyHashes.put(libFile.getFileName().toString(), hash);
}
}
// Create manifest content
StringBuilder manifest = new StringBuilder();
manifest.append("application.jar=").append(jarHash).append("\n");
dependencyHashes.forEach((name, hash) ->
manifest.append("lib/").append(name).append("=").append(hash).append("\n"));
resources.produce(new GeneratedResourceBuildItem(
"META-INF/quarkus-hash-manifest.txt",
manifest.toString().getBytes(StandardCharsets.UTF_8)
));
}
@BuildStep
void processConfigurationKeys(ConfigurationBuildItem config,
BuildProducer<ConfigKeyBuildItem> configKeys) {
// Convert configuration keys to different naming conventions
for (String propertyName : config.getPropertyNames()) {
String kebabCase = StringUtil.toKebabCase(propertyName);
String camelCase = StringUtil.toCamelCase(propertyName);
configKeys.produce(new ConfigKeyBuildItem(propertyName, kebabCase, camelCase));
}
}Utilities for working with JavaBeans and property introspection.
class JavaBeanUtil {
/**
* Gets bean properties for class
*/
static List<PropertyDescriptor> getBeanProperties(Class<?> beanClass);
/**
* Checks if method is a getter
*/
static boolean isGetter(Method method);
/**
* Checks if method is a setter
*/
static boolean isSetter(Method method);
/**
* Gets property name from getter/setter method
*/
static String getPropertyName(Method method);
/**
* Gets property type
*/
static Class<?> getPropertyType(PropertyDescriptor property);
/**
* Sets property value using setter
*/
static void setProperty(Object bean, String propertyName, Object value);
/**
* Gets property value using getter
*/
static Object getProperty(Object bean, String propertyName);
}Factory for creating runtime proxies for interfaces.
class ProxyFactory {
/**
* Creates proxy for interface with invocation handler
*/
static <T> T createProxy(Class<T> interfaceType,
InvocationHandler handler,
ClassLoader classLoader);
/**
* Creates proxy class for interface
*/
static Class<?> createProxyClass(Class<?>[] interfaces,
ClassLoader classLoader);
/**
* Checks if class is a proxy class
*/
static boolean isProxyClass(Class<?> clazz);
}Usage Examples:
@BuildStep
void processConfigurationBeans(CombinedIndexBuildItem index,
BuildProducer<ConfigBeanBuildItem> configBeans) {
// Find classes annotated with @ConfigProperties
Collection<AnnotationInstance> configProperties = index.getIndex()
.getAnnotations(DotName.createSimple("io.quarkus.arc.config.ConfigProperties"));
for (AnnotationInstance annotation : configProperties) {
String className = annotation.target().asClass().name().toString();
try {
Class<?> configClass = Class.forName(className);
// Get bean properties
List<PropertyDescriptor> properties = JavaBeanUtil.getBeanProperties(configClass);
for (PropertyDescriptor property : properties) {
String configKey = StringUtil.toKebabCase(property.getName());
configBeans.produce(new ConfigBeanBuildItem(
configClass,
property.getName(),
configKey,
property.getPropertyType()
));
}
} catch (ClassNotFoundException e) {
// Handle missing class
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-io--quarkus--quarkus-core-deployment