Quarkus (https://quarkus.io/) is a Kubernetes Native Java stack tailored for GraalVM & OpenJDK HotSpot, crafted from the best of breed Java libraries and standards. Also focused on developer experience, making things just work with little to no configuration and allowing to do live coding.
In this page you can either read what has been added in each version of the cheat-sheet individually in [whatsnew] section, see the full cheat-sheet as HTML Quarkus Cheat-Sheet format or download the full cheat-sheet as PDF.
Quarkus Cheat-Sheet
- Quarkus Cheat-Sheet
- What is Quarkus?
- Getting Started
- Gradle
- Packaging
- Command mode
- Extensions
- Application Lifecycle
- Dev Mode
- Dev Services
- Dev UI
- Continuous Testing
- Adding Configuration Parameters
- Undertow Properties
- Injection
- JSON Marshalling/Unmarshalling
- XML Marshalling/Unmarshalling
- JAXP
- Validator
- Logging
- Rest Client
- Testing
- Persistence
- Hibernate Envers
- REST Data Panache
- Hibernate Reactive
- Infinispan
- Redis
- Flyway
- Liquibase
- Hibernate Search
- Amazon DynamoDB
- Amazon S3
- Neo4j
- MongoDB Client
- MongoDB Panache
- MongoDB REST Data Panache
- Cassandra
- Reactive Programming
- RESTEasy Reactive
- Reactive Messaging
- Kafka Streams
- Reactive DataSource Properties
- Reactive PostgreSQL Client
- Reactive MySQL Client
- Reactive DB2 Client
- Reactive Transactions
- ActiveMQ Artemis
- Vert.X Reactive Clients
- Amazon SQS Client
- TLS
- RBAC
- BouncyCastle
- JWT
- OpenId Connect
- OAuth2
- Authenticating via HTTP
- Security with Properties File
- Security with a JDBC Realm
- Security with JPA
- Security with LDAP
- Vault
- Amazon KMS
- Amazon IAM
- HTTP Configuration
- JAX-RS
- Vert.X Filters and Routes
- GraphQL
- Vert.X Verticle
- GZip Support
- GRPC
- Smallrye Stork
- Fault Tolerance
- Observability
- Native Executable
- Container Images Creation
- Kubernetes
- Minikube
- OpenShift
- Kubernetes Configuration Extension
- Kubernetes Client
- AWS Lambda
- Azure Functions
- Funqy
- Funqy HTTP
- Funqy Cloud Events
- Apache Camel
- WebSockets
- OpenAPI
- Mail Sender
- Scheduled Tasks
- Kogito
- Apache Tika
- JGit
- Web Resources
- Transactional Memory
- Quartz
- Qute
- Sentry
- JSch
- Cache
- Banner
- OptaPlanner
- Context Propagation
- Configuration from HasiCorp Consul
- Amazon Alexa
- WebJar Locator
- Amazon SES
- Jbang
- Narayana LRA
- Spring DI
- Spring Boot Configuration
- Spring Cloud Config Client
- Spring Web
- Spring Data JPA
- Spring Data Rest
- Spring Security
- Spring Cache
- Spring Schedule
- Resources
What is Quarkus?
Quarkus is a Kubernetes Native Java stack tailored for GraalVM & OpenJDK HotSpot, crafted from the best of breed Java libraries and standards. Also focused on developer experience, making things just work with little to no configuration and allowing to do live coding.
Cheat-sheet tested with Quarkus 2.7.0.Final.
Getting Started
Quarkus comes with a Maven archetype to scaffold a very simple starting project.
mvn io.quarkus:quarkus-maven-plugin:2.7.0.Final:create \
-DprojectGroupId=org.acme \
-DprojectArtifactId=getting-started \
-DclassName="org.acme.quickstart.GreetingResource" \
-Dpath="/hello"
This creates a simple JAX-RS resource called GreetingResource
.
@Path("/hello")
public class GreetingResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "hello";
}
}
Gradle
There is no way to scaffold a project in Gradle but you only need to do:
plugins {
id 'java'
id 'io.quarkus' version '0.26.1'
}
repositories {
mavenCentral()
}
dependencies {
implementation enforcedPlatform('io.quarkus:quarkus-bom:0.26.1')
implementation 'io.quarkus:quarkus-resteasy'
}
Or in Kotlin:
plugins {
java
}
apply(plugin = "io.quarkus")
repositories {
mavenCentral()
}
dependencies {
implementation(enforcedPlatform("io.quarkus:quarkus-bom:0.26.1"))
implementation("io.quarkus:quarkus-resteasy")
}
Packaging
mvn clean package
You need to distribute the -runner.jar
file together with quarkus-app
directory.
If quarkus.package.uber-jar
property is set to true, then a uber-jar is created with all dependencies bundled inside the JAR.
By default, Quarkus uses fast jar packaging, if quarkus.package.type
property is set to legacy-jar
then Quarkus creates the old standard jar file.
quarkus.package.uber-jar=true
quarkus.package.type=legacy-jar
To compile to native, you need to set GRAALVM_HOME
environment variable and run the native
profile.
mvn clean package -Pnative
./gradlew build -Dquarkus.package.type=native
Possible quarkus.package.type
are: jar
, legacy-jar
, uber-jar
and native
.
AppCDS
Automatically generate AppCDS as part of the build process set the next property: quarkus.package.create-appcds=true
.
To make use of it, just run java -jar -XX:SharedArchiveFile=app-cds.jsa myapp.jar
.
Command mode
You can define the main
CLI method to start Quarkus.
There are two ways, implementing io.quarkus.runtime.QuarkusApplication
interface or use the Java main method to launch Quarkus.
@io.quarkus.runtime.annotations.QuarkusMain
public class HelloWorldMain implements QuarkusApplication {
@Override
public int run(String... args) throws Exception {
System.out.println("Hello World");
return 10;
}
}
run
method called when Quarkus starts, and stops when it finishes.
As Java main
:
@QuarkusMain
public class JavaMain {
public static void main(String... args) {
Quarkus.run(HelloWorldMain.class, args);
}
}
Use @QuarkusMain in only one place.
|
Use Quarkus.waitForExit()
from the main thread if you want to run some logic on startup, and then run like a normal application (i.e. not exit).
You can inject command line arguments by using @CommandLineArguments
annotation:
@CommandLineArguments
String[] args;
Testing
Command Mode applications can be tested using the @QuarkusMainTest
and @QuarkusMainIntegrationTest
annotations.
import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
import io.quarkus.test.junit.main.QuarkusMainLauncher;
import io.quarkus.test.junit.main.QuarkusMainTest;
@QuarkusMainTest
public class HelloTest {
@Test
@Launch("World")
public void testLaunchCommand(LaunchResult result) {
Assertions.assertEquals("Hello World", result.getOutput());
}
@Test
@Launch(value = {}, exitCode = 1)
public void testLaunchCommandFailed() {
}
@Test
public void testManualLaunch(QuarkusMainLauncher launcher) {
LaunchResult result = launcher.launch("Everyone");
Assertions.assertEquals(0, result.exitCode());
Assertions.assertEquals("Hello Everyone", result.getOutput());
}
}
Picocli
You can use Picocli to implement CLI applications:
./mvnw quarkus:add-extension
-Dextensions="picocli"
@CommandLine.Command
public class HelloCommand implements Runnable {
@CommandLine.Option(names = {"-n", "--name"}, description = "Who will we greet?", defaultValue = "World")
String name;
private final GreetingService greetingService;
public HelloCommand(GreetingService greetingService) {
this.greetingService = greetingService;
}
@Override
public void run() {
greetingService.sayHello(name);
}
}
All classes annotated with picocli.CommandLine.Command
are registered as CDI beans.
If only one class annotated with picocli.CommandLine.Command
it will be used as entry point.
If you want to provide your own @QuarkusMain
:
@QuarkusMain
@CommandLine.Command(name = "demo", mixinStandardHelpOptions = true)
public class ExampleApp implements Runnable, QuarkusApplication {
@Inject
CommandLine.IFactory factory;
@Override
public void run() {
}
@Override
public int run(String... args) throws Exception {
return new CommandLine(this, factory).execute(args);
}
}
Use quarkus.picocli.native-image.processing.enable
to false
to use the picocli-codegen
annotation processor instead of build steps.
You can also configure CDI beans with PicoCLI arguments:
@CommandLine.Command
public class EntryCommand implements Runnable {
@CommandLine.Option(names = "-c", description = "JDBC connection string")
String connectionString;
@Inject
DataSource dataSource;
}
@ApplicationScoped
class DatasourceConfiguration {
@Produces
@ApplicationScoped
DataSource dataSource(CommandLine.ParseResult parseResult) {
System.out.println(parseResult.matchedOption("c").getValue().toString());
}
}
Extensions
Quarkus comes with extensions to integrate with some libraries such as JSON-B, Camel or MicroProfile spec. To list all available extensions just run:
./mvnw quarkus:list-extensions
You can use -DsearchPattern=panache to filter out all extensions except the ones matching the expression.
|
And to register the extensions into build tool:
./mvnw quarkus:add-extension -Dextensions=""
./mvnw quarkus:remove-extension -Dextensions=""
extensions property supports CSV format to register more than one extension at once.
|
Application Lifecycle
You can be notified when the application starts/stops by observing StartupEvent
and ShutdownEvent
events.
@ApplicationScoped
public class ApplicationLifecycle {
void onStart(@Observes StartupEvent event) {}
void onStop(@Observes ShutdownEvent event) {}
}
Quarkus supports graceful shutdown.
By default there is no timeout but can be set by using the quarkus.shutdown.timeout
config property.
Dev Mode
./mvnw compile quarkus:dev
./gradlew quarkusDev
Endpoints are registered automatically to provide some basic debug info in dev mode:
-
HTTP GET /quarkus/arc/beans
-
Query Parameters:
scope
,beanClass
,kind
.
-
-
HTTP GET /quarkus/arc/observers
Dev Services
Quarkus supports the automatic provisioning of unconfigured services in development and test mode.
General configuration properties:
quarkus.devservices.enabled
-
If you want to disable all Dev Services. (default:
true
) quarkus.devservices.timeout
-
Startup timeout. (default:
60S
)
Set DOCKER_HOST
env var to a remote Docker Host to use it instead the local one.
Dev UI
Quarkus adds a Dev UI console to expose extension features.
The Dev UI is available in dev mode only and accessible at the /q/dev
endpoint by default.
Continuous Testing
Quarkus supports continuous testing, where tests run immediately after code changes have been saved. This allows you to get instant feedback on your code changes. Quarkus detects which tests cover which code, and uses this information to only run the relevant tests when code is changed.
Start Quarkus in Dev Mode to run it.
Also, you can start continuous testing and not dev mode by running mvn quarkus:test
.
quarkus.test.exclude-pattern
-
Tests that should be excluded with continuous testing default:
.*\.IT[^.]+|.*IT|.*ITCase
Adding Configuration Parameters
To add configuration to your application, Quarkus relies on MicroProfile Config spec.
@ConfigProperty(name = "greetings.message")
String message;
@ConfigProperty(name = "greetings.message",
defaultValue = "Hello")
String messageWithDefault;
@ConfigProperty(name = "greetings.message")
Optional<String> optionalMessage;
Properties can be set (in decreasing priority) as:
-
System properties (
-Dgreetings.message
). -
Environment variables (
GREETINGS_MESSAGE
). -
Environment file named
.env
placed in the current working directory (GREETING_MESSAGE=
). -
External config directory under the current working directory:
config/application.properties
. -
Resources
src/main/resources/application.properties
.
greetings.message = Hello World
Array , List and Set are supported. The delimiter is comma (, ) char and \ is the escape char.
|
Configuration Profiles
Quarkus allow you to have multiple configuration in the same file (application.properties
).
The syntax for this is %{profile}.config.key=value
.
quarkus.http.port=9090
%dev.quarkus.http.port=8181
HTTP port will be 9090, unless the 'dev' profile is active.
Default profiles are:
-
dev
: Activated when in development mode (quarkus:dev
). -
test
: Activated when running tests. -
prod
: The default profile when not running in development or test mode
You can create custom profile names by enabling the profile either setting quarkus.profile
system property or QUARKUS_PROFILE
environment variable.
quarkus.http.port=9090
%staging.quarkus.http.port=9999
And enable it quarkus.profile=staging
.
To get the active profile programmatically use io.quarkus.runtime.configuration.ProfileManager.getActiveProfile() .
|
You can also set it in the build tool:
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<systemPropertyVariables>
<quarkus.test.profile>foo</quarkus.test.profile>
<buildDirectory>${project.build.directory}
</buildDirectory>
</systemPropertyVariables>
</configuration>
Same for maven-failsafe-plugin .
|
test {
useJUnitPlatform()
systemProperty "quarkus.test.profile", "foo"
}
Special properties are set in prod mode: quarkus.application.version
and quarkus.application.name
to get them available at runtime.
@ConfigProperty(name = "quarkus.application.name")
String applicationName;
Additional locations
You can use smallrye.config.locations
property to set additional configuration files.
smallrye.config.locations=config.properties
or
java -jar -Dsmallrye.config.locations=config.properties
You can embed configuration files inside a dependency by adding META-INF/microprofile.properties
inside the JAR.
When dependency is added to the application, configuration properties are merged with current configuration.
@ConfigProperties
As an alternative to injecting multiple related configuration values, you can also use the @io.quarkus.arc.config.ConfigProperties
annotation to group properties.
@ConfigProperties(prefix = "greeting", namingStrategy=NamingStrategy.KEBAB_CASE)
public class GreetingConfiguration {
private String message;
// getter/setter
}
This class maps greeting.message
property defined in application.properties
.
You can inject this class by using CDI @Inject GreetingConfiguration greeting;
.
@IfBuildProfile
, @UnlessBuildProfile
, @IfBuildProperty
and @UnlessBuildProperty
annotations are used to define when configuration properties are valid.
@UnlessBuildProfile("test")
@ConfigProperties
public class ServiceConfiguration {
public String user;
public String password;
}
Also you can use an interface approach:
@ConfigProperties(prefix = "greeting", namingStrategy=NamingStrategy.KEBAB_CASE)
public interface GreetingConfiguration {
@ConfigProperty(name = "message")
String message();
String getSuffix();
If property does not follow getter/setter naming convention you need to use org.eclipse.microprofile.config.inject.ConfigProperty
to set it.
Nested objects are also supporte:
@ConfigProperties(prefix = "greeting", namingStrategy=NamingStrategy.KEBAB_CASE)
public class GreetingConfiguration {
public String message;
public HiddenConfig hidden;
public static class HiddenConfig {
public List<String> recipients;
}
}
And an application.properties
mapping previous class:
greeting.message = hello
greeting.hidden.recipients=Jane,John
Bean Validation is also supported so properties are validated at startup time, for example @Size(min = 20) public String message;
.
prefix attribute is not mandatory. If not provided, attribute is determined by class name (ie GreeetingConfiguration is translated to greeting or GreetingExtraConfiguration to greeting-extra ). The suffix of the class is always removed.
|
Naming strategy can be changed with property namingStrategy
. KEBAB_CASE
(whatever.foo-bar) or VERBATIM
(whatever.fooBar).
@io.quarkus.arc.config.ConfigIgnore
annotation can be used to ignore the injection of configuration elements.
@ConfigIgnore
public Integer ignored;
YAML Config
YAML configuration is also supported.
The configuration file is called application.yaml
and you need to register a dependency to enable its support:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-config-yaml</artifactId>
</dependency>
quarkus:
datasource:
url: jdbc:postgresql://localhost:5432/some-database
driver: org.postgresql.Driver
Or with profiles:
"%dev":
quarkus:
datasource:
url: jdbc:postgresql://localhost:5432/some-database
driver: org.postgresql.Driver
In case of subkeys ~
is used to refer to the unprefixed part.
quarkus:
http:
cors:
~: true
methods: GET,PUT,POST
Is equivalent to:
quarkus.http.cors=true
quarkus.http.cors.methods=GET,PUT,POST
Custom Loader
You can implement your own ConfigSource
to load configuration from different places than the default ones provided by Quarkus.
For example, database, custom XML, REST Endpoints, …
You need to create a new class and implement ConfigSource
interface:
package com.acme.config;
public class InMemoryConfig implements ConfigSource {
private Map<String, String> prop = new HashMap<>();
public InMemoryConfig() {
// Init properties
}
@Override
public int getOrdinal() {
// The highest ordinal takes precedence
return 900;
}
@Override
public Map<String, String> getProperties() {
return prop;
}
@Override
public String getValue(String propertyName) {
return prop.get(propertyName);
}
@Override
public String getName() {
return "MemoryConfigSource";
}
}
Then you need to register the ConfigSource
as Java service.
Create a file with the following content:
com.acme.config.InMemoryConfig
Custom Converters
You can implement your own conversion types from String.
Implement org.eclipse.microprofile.config.spi.Converter
interface:
@Priority(DEFAULT_QUARKUS_CONVERTER_PRIORITY + 100)
public class CustomInstantConverter
implements Converter<Instant> {
@Override
public Instant convert(String value) {
if ("now".equals(value.trim())) {
return Instant.now();
}
return Instant.parse(value);
}
}
@Priority
annotation is used to override the default InstantConverter
.
Then you need to register the Converter
as Java service.
Create a file with the following content:
com.acme.config.CustomInstantConverter
Undertow Properties
Possible parameters with prefix quarkus.servlet
:
context-path
-
The context path to serve all Servlet context from. (default:
/
) default-charset
-
The default charset to use for reading and writing requests. (default:
UTF-8
)
Injection
Quarkus is based on CDI 2.0 to implement injection of code. It is not fully supported and only a subset of the specification is implemented.
@ApplicationScoped
public class GreetingService {
public String message(String message) {
return message.toUpperCase();
}
}
Scope annotation is mandatory to make the bean discoverable.
@Inject
GreetingService greetingService;
Quarkus is designed with Substrate VM in mind. For this reason, we encourage you to use package-private scope instead of private. |
Produces
You can also create a factory of an object by using @javax.enterprise.inject.Produces
annotation.
@Produces
@ApplicationScoped
Message message() {
Message m = new Message();
m.setMsn("Hello");
return m;
}
@Inject
Message msg;
Qualifiers
You can use qualifiers to return different implementations of the same interface or to customize the configuration of the bean.
@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Quote {
@Nonbinding String value();
}
@Produces
@Quote("")
Message message(InjectionPoint msg) {
Message m = new Message();
m.setMsn(
msg.getAnnotated()
.getAnnotation(Quote.class)
.value()
);
return m;
}
@Inject
@Quote("Aloha Beach")
Message message;
Quarkus breaks the CDI spec by allowing you to inject qualified beans without using @Inject annotation.
|
@Quote("Aloha Beach")
Message message;
Quarkus breaks the CDI spec by skipping the @Produces annotation completely if the producer method is annotated with a scope annotation, a stereotype or a qualifier.
|
@Quote("")
Message message(InjectionPoint msg) {
}
@Quote("Aloha Beach")
Message message;
@LookupIfProperty
indicates that a bean should only be obtained if a runtime configuration property matches the provided value.
@LookupUnlessProperty
, on the other hand, indicates that a bean should only be obtained if a runtime configuration property does not match the provided value.
@LookupIfProperty(name = "service.foo.enabled", stringValue = "true")
@ApplicationScoped
class ServiceFoo implements Service {
public String name() {
return "foo";
}
}
Alternatives
It is also possible to select alternatives for an application using application.properties
.
quarkus.arc.selected-alternatives=org.acme.Foo,org.acme.*,Bar
Beans by Quarkus Profile
Using @io.quarkus.arc.profile.IfBuildProfile
and @io.quarkus.arc.profile.UnlessBuildProfile
annotations, you can conditionally enable a bean.
@Dependent
public class TracerConfiguration {
@Produces
@IfBuildProfile("prod")
public Tracer realTracer(Reporter reporter, Configuration configuration) {
return new RealTracer(reporter, configuration);
}
@Produces
@DefaultBean
public Tracer noopTracer() {
return new NoopTracer();
}
}
Using @io.quarkus.arc.profile.IfBuildProperty
annotation, you can conditionally enable a bean.
@io.quarkus.arc.DefaultBean
sets the default bean.
@Dependent
public class TracerConfiguration {
@Produces
@IfBuildProperty(name = "some.tracer.enabled", stringValue = "true")
public Tracer realTracer(Reporter reporter, Configuration configuration) {}
@Produces
@DefaultBean
public Tracer noopTracer() {}
}
Properties set at runtime have absolutely no effect on the bean resolution using @IfBuildProperty .
|
Container-managed Concurrency
Quarkus provides @io.quarkus.arc.Lock
and a built-in interceptor for concurrency control.
@Lock
@ApplicationScoped
class SharedService {
void addAmount(BigDecimal amout) {
}
@Lock(value = Lock.Type.READ, time = 1, unit = TimeUnit.SECONDS)
BigDecimal getAmount() {
}
}
By default the class is in write mode (so no concurrent calls allowed) except when lock type is READ
where the method can be called concurrently if no write operation in process.
JSON Marshalling/Unmarshalling
To work with JSON-B
you need to add a dependency:
./mvnw quarkus:add-extension
-Dextensions="io.quarkus:quarkus-resteasy-jsonb"
Any POJO is marshaled/unmarshalled automatically.
public class Sauce {
private String name;
private long scovilleHeatUnits;
// getter/setters
}
JSON equivalent:
{
"name":"Blair's Ultra Death",
"scovilleHeatUnits": 1100000
}
In a POST
endpoint example:
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response create(Sauce sauce) {
// Create Sauce
return Response.created(URI.create(sauce.getId()))
.build();
}
To provide custom JsonBConfig object:
@Dependent
JsonbConfig jsonConfig(Instance<JsonbConfigCustomizer> customizers) {
JsonbConfig config = myJsonbConfig(); // Custom `JsonbConfig`
for (JsonbConfigCustomizer customizer : customizers) {
customizer.customize(config);
}
return config;
}
To work with Jackson
you need to add:
./mvnw quarkus:add-extension
-Dextensions="quarkus-resteasy-jackson"
If you don’t want to use the default ObjectMapper
you can customize it by:
@ApplicationScoped
public class CustomObjectMapperConfig {
@Singleton
@Produces
public ObjectMapper objectMapper(Instance<ObjectMapperCustomizer> customizers) {
ObjectMapper objectMapper = new ObjectMapper();
// perform configuration
for (ObjectMapperCustomizer customizer : customizers) {
customizer.customize(mapper);
}
return objectMapper;
}
}
Default media type in Quarkus RestEasy is JSON. |
XML Marshalling/Unmarshalling
To work with JAX-B
you need to add a dependency:
./mvnw quarkus:add-extension
-Dextensions="quarkus-resteasy-jaxb"
Then annotated POJOs are converted to XML.
@XmlRootElement
public class Message {
}
@GET
@Produces(MediaType.APPLICATION_XML)
public Message hello() {
return message;
}
JAXP
To work with JAX-P
you need to add a dependency:
./mvnw quarkus:add-extension
-Dextensions="jaxp"
final DocumentBuilder dBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
final Document doc = dBuilder.parse(in);
return doc.getDocumentElement().getTextContent();
Validator
Quarkus uses Hibernate Validator to validate input/output of REST services and business services using Bean validation spec.
./mvnw quarkus:add-extension
-Dextensions="io.quarkus:quarkus-hibernate-validator"
Annotate POJO objects with validator annotations such as: @NotNull
, @Digits
, @NotBlank
, @Min
, @Max
, …
public class Sauce {
@NotBlank(message = "Name may not be blank")
private String name;
@Min(0)
private long scovilleHeatUnits;
// getter/setters
}
To validate an object use @Valid
annotation:
public Response create(@Valid Sauce sauce) {}
If a validation error is triggered, a violation report is generated and serialized as JSON. If you want to manipulate the output, you need to catch in the code the ConstraintViolationException exception.
|
Create Your Custom Constraints
First you need to create the custom annotation:
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR,
PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = { NotExpiredValidator.class})
public @interface NotExpired {
String message() default "Sauce must not be expired";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
You need to implement the validator logic in a class that implements ConstraintValidator
.
public class NotExpiredValidator
implements ConstraintValidator<NotExpired, LocalDate>
{
@Override
public boolean isValid(LocalDate value,
ConstraintValidatorContext ctx) {
if ( value == null ) return true;
LocalDate today = LocalDate.now();
return ChronoUnit.YEARS.between(today, value) > 0;
}
}
And use it normally:
@NotExpired
@JsonbDateFormat(value = "yyyy-MM-dd")
private LocalDate expired;
Manual Validation
You can call the validation process manually instead of relaying to @Valid
by injecting Validator
class.
@Inject
Validator validator;
And use it:
Set<ConstraintViolation<Sauce>> violations =
validator.validate(sauce);
Localization
You can configure the based locale for validation messages.
quarkus.default-locale=ca-ES
# Supported locales resolved by Accept-Language
quarkus.locales=en-US,es-ES,fr-FR, ca_ES
pattern.message=No conforme al patro
@Pattern(regexp = "A.*", message = "{pattern.message}")
private String name;
Bean Validation can be configured .
The prefix is: quarkus.hibernate-validator
.
fail-fast
-
When fail fast is enabled the validation will stop on the first constraint violation detected. (default:
false
) method-validation.allow-overriding-parameter-constraints
-
Define whether overriding methods that override constraints should throw an exception. (default:
false
). method-validation.allow-parameter-constraints-on-parallel-methods
-
Define whether parallel methods that define constraints should throw an exception. (default:
false
). method-validation.allow-multiple-cascaded-validation-on-return-values
-
Define whether more than one constraint on a return value may be marked for cascading validation are allowed. (default:
false
).
Logging
You can configure how Quarkus logs:
quarkus.log.console.enable=true
quarkus.log.console.level=DEBUG
quarkus.log.console.color=false
quarkus.log.category."com.lordofthejars".level=DEBUG
Prefix is quarkus.log
.
category."<category-name>".level
-
Minimum level category (default:
INFO
) level
-
Default minimum level (default:
INFO
) console.enabled
-
Console logging enabled (default:
true
) console.format
-
Format pattern to use for logging. Default value:
%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c{3.}] (%t) %s%e%n
console.level
-
Minimum log level (default:
INFO
) console.color
-
Allow color rendering (default:
true
) console.stderr
-
If console logging should go to System#err instead of System#out (default:
false
) file.enable
-
File logging enabled (default:
false
) file.format
-
Format pattern to use for logging. Default value:
%d{yyyy-MM-dd HH:mm:ss,SSS} %h %N[%i] %-5p [%c{3.}] (%t) %s%e%n
file.level
-
Minimum log level (default:
ALL
) file.path
-
The path to log file (default:
quarkus.log
) file.rotation.max-file-size
-
The maximum file size of the log file
file.rotation.max-backup-index
-
The maximum number of backups to keep (default:
1
) file.rotation.file-suffix
-
Rotating log file suffix.
file.rotation.rotate-on-boot
-
Indicates rotate logs at bootup (default:
true
) file.async
-
Log asynchronously (default:
false
) file.async.queue-length
-
The queue length to use before flushing writing (default:
512
) file.async.overflow
-
Action when queue is full (default:
BLOCK
) syslog.enable
-
syslog logging is enabled (default:
false
) syslog.format
-
The format pattern to use for logging to syslog. Default value:
%d{yyyy-MM-dd HH:mm:ss,SSS} %h %N[%i] %-5p [%c{3.}] (%t) %s%e%n
syslog.level
-
The minimum log level to write to syslog (default:
ALL
) syslog.endpoint
-
The IP address and port of the syslog server (default:
localhost:514
) syslog.app-name
-
The app name used when formatting the message in RFC5424 format (default: current process name)
syslog.hostname
-
The name of the host the messages are being sent from (default: current hostname)
syslog.facility
-
Priority of the message as defined by RFC-5424 and RFC-3164 (default:
USER_LEVEL
) syslog.syslog-type
-
The syslog type of format message (default:
RFC5424
) syslog.protocol
-
Protocol used (default:
TCP
) syslog.use-counting-framing
-
Message prefixed with the size of the message (default
false
) syslog.truncate
-
Message should be truncated (default:
true
) syslog.block-on-reconnect
-
Block when attempting to reconnect (default:
true
) syslog.async
-
Log asynchronously (default:
false
) syslog.async.queue-length
-
The queue length to use before flushing writing (default:
512
) syslog.async.overflow
-
Action when queue is full (default:
BLOCK
)
You can inject logger instance:
import org.jboss.logging.Logger;
import io.quarkus.arc.log.LoggerName;
@Inject
Logger log;
@LoggerName("foo")
Logger fooLog;
public void ping() {
log.info("Simple!");
}
Gelf ouput
You can configure the output to be in GELF format instead of plain text.
./mvnw quarkus:add-extension
-Dextensions="quarkus-logging-gelf"
handler.gelf.enabled
-
Enable GELF logging handler (default:
false
) handler.gelf.host
-
Hostname/IP of Logstash/Graylof. Prepend
tcp:
for using TCP protocol. (default:udp:localhost
) handler.gelf.port
-
The port. (default:
12201
) handler.gelf.version
-
GELF version. (default:
1.1
) handler.gelf.extract-stack-trace
-
Post Stack-Trace to StackTrace field. (default:
true
) handler.gelf.stack-trace-throwable-reference
-
Gets the cause level to stack trace.
0
is fulls tack trace. (default:0
) handler.gelf.filter-stack-trace
-
Stack-Trace filtering. (default:
false
) handler.gelf.timestamp-pattern
-
Java Date pattern. (default:
yyyy-MM-dd HH:mm:ss,SSS
) handler.gelf.level
-
Log level
java.util.logging.Level
. (default:ALL
) handler.gelf.facility
-
Name of the facility. (default:
jboss-logmanage
) handler.gelf.additional-field.<field>.<subfield>
-
Post additional fields.
quarkus.log.handler.gelf.additional-field.field1.type=String
handler.gelf.include-full-mdc
-
Include all fields from the MDC.
handler.gelf.maximum-message-size
-
Maximum message size (in bytes). (default:
8192
) handler.gelf.include-log-message-parameters
-
Include message parameters from the log event. (default:
true
) handler.gelf.include-location
-
Include source code location. (default:
true
)
JSON output
You can configure the output to be in JSON format instead of plain text.
./mvnw quarkus:add-extension
-Dextensions="quarkus-logging-json"
And the configuration values are prefix with quarkus.log
:
json
-
JSON logging is enabled (default: true).
json.pretty-print
-
JSON output is "pretty-printed" (default: false)
json.date-format
-
Specify the date format to use (default: the default format)
json.record-delimiter
-
Record delimiter to add (default: no delimiter)
json.zone-id
-
The time zone ID
json.exception-output-type
-
The exception output type:
detailed
,formatted
,detailed-and-formatted
(default:detailed
) json.print-details
-
Detailed caller information should be logged (default: false)
Rest Client
Quarkus implements MicroProfile Rest Client spec:
./mvnw quarkus:add-extension
-Dextensions="quarkus-rest-client"
To get content from http://worldclockapi.com/api/json/cet/now you need to create a service interface:
@Path("/api")
@RegisterRestClient
public interface WorldClockService {
@GET @Path("/json/cet/now")
@Produces(MediaType.APPLICATION_JSON)
WorldClock getNow();
@GET
@Path("/json/{where}/now")
@Produces(MediaType.APPLICATION_JSON)
WorldClock getSauce(@BeanParam
WorldClockOptions worldClockOptions);
}
public class WorldClockOptions {
@HeaderParam("Authorization")
String auth;
@PathParam("where")
String where;
}
And configure the hostname at application.properties
:
org.acme.quickstart.WorldClockService/mp-rest/url=
http://worldclockapi.com
Injecting the client:
@RestClient
WorldClockService worldClockService;
If invokation happens within JAX-RS, you can propagate headers from incoming to outgoing by using next property.
org.eclipse.microprofile.rest.client.propagateHeaders=
Authorization,MyCustomHeader
You can still use the JAX-RS client without any problem ClientBuilder.newClient().target(…)
|
Adding headers
You can customize the headers passed by implementing MicroProfile ClientHeadersFactory
annotation:
@RegisterForReflection
public class BaggageHeadersFactory
implements ClientHeadersFactory {
@Override
public MultivaluedMap<String, String> update(
MultivaluedMap<String, String> incomingHeaders,
MultivaluedMap<String, String> outgoingHeaders) {}
}
And registering it in the client using RegisterClientHeaders
annotation.
@RegisterClientHeaders(BaggageHeadersFactory.class)
@RegisterRestClient
public interface WorldClockService {}
Or statically set:
@GET
@ClientHeaderParam(name="X-Log-Level", value="ERROR")
Response getNow();
Asynchronous
A method on client interface can return a CompletionStage
class to be executed asynchronously.
@GET @Path("/json/cet/now")
@Produces(MediaType.APPLICATION_JSON)
CompletionStage<WorldClock> getNow();
Reactive
Rest Client also integrates with reactive library named Mutiny.
To start using it you need to add the quarkus-rest-client-mutiny
.
After that, a methodon a client interface can return a io.smallrye.mutiny.Uni
instance.
@GET @Path("/json/cet/now")
@Produces(MediaType.APPLICATION_JSON)
Uni<WorldClock> getNow();
A RESTEasy Reactive-based REST Client extension.
You only need to replace the quarkus-rest-client
to quarkus-rest-client-reactive
.
Multipart
It is really easy to send multipart form-data with Rest Client.
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-multipart-provider</artifactId>
</dependency>
The model object:
import java.io.InputStream;
import javax.ws.rs.FormParam;
import javax.ws.rs.core.MediaType;
import
org.jboss.resteasy.annotations.providers.multipart.PartType;
public class MultipartBody {
@FormParam("file")
@PartType(MediaType.APPLICATION_OCTET_STREAM)
private InputStream file;
@FormParam("fileName")
@PartType(MediaType.TEXT_PLAIN)
private String name;
// getter/setters
}
And the Rest client interface:
import
org.jboss.resteasy.annotations.providers.multipart.MultipartForm;
@Path("/echo")
@RegisterRestClient
public interface MultipartService {
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
String sendMultipartData(@MultipartForm
MultipartBody data);
}
SSL
You can configure Rest Client key stores.
org.acme.quickstart.WorldClockService/mp-rest/trustStore=
classpath:/store.jks
org.acme.quickstart.WorldClockService/mp-rest/trustStorePassword=
supersecret
Possible configuration properties:
%s/mp-rest/trustStore
-
Trust store location defined with
classpath:
orfile:
prefix. %s/mp-rest/trustStorePassword
-
Trust store password.
%s/mp-rest/trustStoreType
-
Trust store type (default:
JKS
) %s/mp-rest/hostnameVerifier
-
Custom hostname verifier class name. To disable SSL verification you can use
io.quarkus.restclient.NoopHostnameVerifier
. %s/mp-rest/keyStore
-
Key store location defined with
classpath:
orfile:
prefix. %s/mp-rest/keyStorePassword
-
Key store password.
%s/mp-rest/keyStoreType
-
Key store type (default:
JKS
)
Timeout
You can define the timeout of the Rest Client:
org.acme.quickstart.WorldClockService/mp-rest/connectTimeout=
1000
org.acme.quickstart.WorldClockService/mp-rest/readTimeout=
2000
Instantiate client programmatically
MovieReviewService reviewSvc = RestClientBuilder.newBuilder()
.baseUri(apiUri)
.build(WorldClockService.class);
Testing
Quarkus archetype adds test dependencies with JUnit 5 and Rest-Assured library to test REST endpoints.
@QuarkusTest
public class GreetingResourceTest {
@Test
public void testHelloEndpoint() {
given()
.when().get("/hello")
.then()
.statusCode(200)
.body(is("hello"));
}
}
Test port can be set in quarkus.http.test-port
property.
Timeout can be set in quarkus.http.test-timeout
property.
You can also inject the URL where Quarkus is started:
@TestHTTPResource("index.html")
URL url;
@TestHTTPEndpoint(GreetingResource.class)
@TestHTTPResource
URL url;
@QuarkusTest
@TestHTTPEndpoint(GreetingResource.class)
public class GreetingResourceTest {
@Test
public void testHelloEndpoint() {
given()
.when().get()
.then()
.statusCode(200)
.body(is("hello"));
}
}
Root path is calculated automatically, not necessary to explicitly set.
If you want any changes made to be rolled back at the end ofthe test you can use the io.quarkus.test.TestTransaction
annotation.
QuarkusTestProfile
You can define for each Test class a different configuration options.
This implies that the Quarkus service is restarted. |
public class MyProfile implements io.quarkus.test.junit.QuarkusTestProfile {
@Override
public Map<String, String> getConfigOverrides() {
return Map.of("greetings.message", "This is a Test");
}
@Override
public String getConfigProfile() {
return "my-test-profile";
}
@Override
public Set<String> tags() {
return Collections.singleton("test1");
}
}
@QuarkusTest
@TestProfile(MyProfile.class)
public class MyTestClass {
}
quarkus.test.profile.tags
property can be set to limit test execution of test profiles.
If not set all tests are executed.
quarkus.test.profile.tags=test1
Quarkus Test Resource
You can execute some logic before the first test run (start
) and execute some logic at the end of the test suite (stop
).
You need to create a class implementing QuarkusTestResourceLifecycleManager
interface and register it in the test via @QuarkusTestResource
annotation.
public class MyCustomTestResource
implements QuarkusTestResourceLifecycleManager {
@Override
public Map<String, String> start() {
// return system properties that
// should be set for the running test
return Collections.emptyMap();
}
@Override
public void stop() {
}
// optional
@Override
public void inject(Object testInstance) {
}
// optional
@Override
public int order() {
return 0;
}
// optional
@Override
public void setContext(Context context) {
}
@Override
public void inject(TestInjector testInjector) {
testInjector.injectIntoFields(wireMockServer,
new TestInjector.AnnotatedAndMatchesType(InjectWireMock.class, WireMockServer.class));
}
}
public class MyTest {
@InjectWireMock
WireMockServer wireMockServer;
}
Returning new system properties implies running parallel tests in different JVMs. |
And the usage:
@QuarkusTestResource(MyCustomTestResource.class)
public class MyTest {
}
When using multiple QuarkusTestResource
you can set parallel
attribute to true
to start them concurrently.
Testing Callbacks
You can enrich all your @QuarkusTest
classes by implementing the following callback interfaces:
-
io.quarkus.test.junit.callback.QuarkusTestBeforeClassCallback
-
io.quarkus.test.junit.callback.QuarkusTestAfterConstructCallback
-
io.quarkus.test.junit.callback.QuarkusTestBeforeEachCallback
-
io.quarkus.test.junit.callback.QuarkusTestAfterEachCallback
public class SimpleAnnotationCheckerBeforeClassCallback implements QuarkusTestBeforeClassCallback {
@Override
public void beforeClass(Class<?> testClass) {
}
}
And needs to be registered as Java SPI:
io.quarkus.it.main.SimpleAnnotationCheckerBeforeClassCallback
Mocking
If you need to provide an alternative implementation of a service (for testing purposes) you can do it by using CDI @Alternative
annotation using it in the test service placed at src/test/java
:
@Alternative
@Priority(1)
@ApplicationScoped
public class MockExternalService extends ExternalService {}
This does not work when using native image testing. |
A stereotype annotation io.quarkus.test.Mock
is provided declaring @Alternative
, @Priority(1)
and @Dependent
.
Mockito
Instead of creating stubs, you can also create mocks of your services with mockito.
Add the following dependency io.quarkus:quarkus-junit5-mockito
:
@InjectMock
GreetingService greetingService;
@BeforeEach
public void setup() {
Mockito.when(greetingService.greet()).thenReturn("Hi");
}
@Path("/hello")
public class ExampleResource {
@Inject
GreetingService greetingService;
}
Mock is automatically injected and only valid for the defined test class.
@InjectMock
has convertScopes
parameter that if true, then Quarkus will change the scope of the target from Singleton
bean to ApplicationScoped
to make the mockable.
Also spy
is supported:
@InjectSpy
GreetingService greetingService;
Mockito.verify(greetingService, Mockito.times(1)).greet();
REST Client
To Mock REST Client, you need to define the interface with @ApplicationScope
:
@ApplicationScoped
@RegisterRestClient
public interface GreetingService {
}
@InjectMock
@RestClient
GreetingService greetingService;
Mockito.when(greetingService.hello()).thenReturn("hello from mockito");
Interceptors
Tests are actually full CDI beans, so you can apply CDI interceptors:
@QuarkusTest
@Stereotype
@Transactional
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TransactionalQuarkusTest {
}
@TransactionalQuarkusTest
public class TestStereotypeTestCase {}
Decorator
Quarkus also supports CDI Decorators:
@Priority(10)
@Decorator
public class LargeTxAccount implements Account {
@Inject
@Any
@Delegate
Account delegate;
@Inject
LogService logService;
void withdraw(BigDecimal amount) {
delegate.withdraw(amount);
if (amount.compareTo(1000) > 0) {
logService.logWithdrawal(delegate, amount);
}
}
}
Test Coverage
Due the nature of Quarkus to calculate correctly the coverage information with JaCoCo, you might need offline instrumentation.
./mvnw quarkus:add-extension
-Dextensions="quarkus-jacoco "
Possible configuration parameters prefixed quarkus.jacoco
:
data-file
-
The jacoco data file. (default:
jacoco-quarkus.exec
) report
-
If Quarkus should generate the Jacoco report. (default:
true
) output-encoding
-
Encoding of the generated reports. (default:
UTF-8
) title
-
Name of the root node HTML report pages.
footer
-
Footer text used in HTML report pages.
source-encoding
-
Encoding of the source files. (default:
UTF-8
) includes
-
A list of class files to include in the report. (default:
**
) excludes
-
A list of class files to exclude from the report.
report-location
-
The location of the report files. (default:
jacoco-report
)
Native Testing
To test native executables annotate the test with @NativeImageTest
.
Quarkus Integration Tests
@QuarkusIntegrationTest
should be used to launch and test the artifact produced by the Quarkus build.
If the result of a Quarkus build is a JAR then the app is launched as java -jar
, if native is launched as ./application
, if container image is created (JiB, Docker extensions) is launched as docker run
.
You can connect to already running application by using the following properties: quarkus.http.test-host=1.2.3.4
quarkus.http.test-port=4321
Persistence
Quarkus works with JPA(Hibernate) as persistence solution. But also provides an Active Record pattern implementation under Panache project.
To use database access you need to add Quarkus JDBC drivers instead of the original ones.
At this time Apache Derby
, H2
, MariaDB
, MySQL
, MSSQL
and PostgreSQL
drivers are supported.
./mvnw quarkus:add-extension
-Dextensions="io.quarkus:quarkus-hibernate-orm-panache,
io.quarkus:quarkus-jdbc-mariadb"
@Entity
public class Developer extends PanacheEntity {
// id field is implicit
public String name;
}
And configuration in src/main/resources/application.properties
:
quarkus.datasource.jdbc.url=jdbc:mariadb://localhost:3306/mydb
quarkus.datasource.db-kind=mariadb
quarkus.datasource.username=developer
quarkus.datasource.password=developer
quarkus.hibernate-orm.database.generation=update
List of datasource parameters.
quarkus.datasource
as prefix is skipped in the next table.
db-kind
-
Built-in datasource kinds so the JDBC driver is resolved automatically. Possible values:
derby
,h2
,mariadb
,mssql
,mysql
,postgresql
,db2
. username
-
Username to access.
password
-
Password to access.
driver
-
JDBC Driver class. It is not necessary to set if
db-kind
used. credentials-provider
-
Sets a custom credential provider name.
credentials-provider-name
-
It is the
@Named
value of the credentials provider bean. Not necessary if only one implementation. jdbc.url
-
The datasource URL.
jdbc.min-size
-
The datasource pool minimum size. (default:
0
) jdbc.max-size
-
The datasource pool maximum size. (default:
20
) jdbc.initial-size
-
The initial size of the pool.
jdbc.background-validation-interval
-
The interval at which we validate idle connections in the background. (default:
2M
) jdbc.acquisition-timeout
-
The timeout before cancelling the acquisition of a new connection. (default:
5
) jdbc.leak-detection-interval
-
The interval at which we check for connection leaks.
jdbc.idle-removal-interval
-
The interval at which we try to remove idle connections. (default:
5M
) jdbc.max-lifetime
-
The max lifetime of a connection.
jdbc.transaction-isolation-level
-
The transaction isolation level. Possible values:
UNDEFINED
,NONE
,READ_UNCOMMITTED
,READ_COMMITTED
,REPEATABLE_READ
,SERIALIZABLE
. jdbc.detect-statement-leaks
-
Warn when a connection is returned to the pool without the application having closed all open statements. (default:
true
) jdbc.new-connection-sql
-
Query executed when first using a connection.
jdbc.validation-query-sql
-
Query executed to validate a connection.
jdbc.pooling-enabled
-
Disable pooling to prevent reuse of Connections. (default:
true
) jdbc.enable-metrics
-
Enable datasource metrics collection when using
quarkus-smallrye-metrics
extension. jdbc.additional-jdbc-properties.<extraProperty>
-
Unspecified properties to be passed to the JDBC driver when creating new connections.
Hibernate configuration properties.
Prefix quarkus.hibernate-orm
is skipped.
dialect
-
Class name of the Hibernate ORM dialect.
dialect.storage-engine
-
The storage engine when the dialect supports multiple storage engines.
sql-load-script
-
Name of the file containing the SQL statements to execute when starts.
no-file
force Hibernate to skip SQL import. (default:import.sql
) batch-fetch-size
-
The size of the batches. (default: -1 disabled)
maxFetchDepth
-
The maximum depth of outer join fetch tree for single-ended associations.
multitenant
-
Defines the method for multi-tenancy. Possible values:
DATABASE
,NONE
,SCHEMA
. (default:NONE
) multitenant-schema-datasource
-
Defines the name of the data source to use in case of SCHEMA approach.
metadata-builder-contributor
-
Class name of a custom
org.hibernate.boot.spi.MetadataBuilderContributor
implementation. query.query-plan-cache-max-size
-
The maximum size of the query plan cache.
query.default-null-ordering
-
Default precedence of null values in
ORDER BY
. Possible values:none
,first
,last
. (default:none
) database.generation
-
Database schema is generation. Possible values:
none
,create
,drop-and-create
,drop
,update
. (default:none
) database.generation.halt-on-error
-
Stop on the first error when applying the schema. (default:
false
) database.generation.create-schemas
-
Hibernate ORM should create the schemas automatically (for databases supporting them).
database.default-catalog
-
Default catalog.
database.default-schema
-
Default Schema.
database.charset
-
Charset.
scripts.generation
-
Select whether the database schema DDL files are generated or not. Possible values:
none
,create
,drop-and-create
,drop
,update
. (default:none
) scripts.generation.create-target
-
Filename or URL where the database create DDL file should be generated.
scripts.generation.drop-target
-
Filename or URL where the database drop DDL file should be generated.
jdbc.timezone
-
Time Zone JDBC driver.
jdbc.statement-fetch-size
-
Number of rows fetched at a time.
jdbc.statement-batch-size
-
Number of updates sent at a time.
log.sql
-
Show SQL logs (default:
false
) log.jdbc-warnings
statistics
-
Enable statiscs collection. (default:
false
) physical-naming-strategy
-
Class name of the Hibernate PhysicalNamingStrategy implementation.
globally-quoted-identifiers
-
Should quote all identifiers. (default:
false
)
metrics-enabled
-
Metrics published with smallrye-metrics extension (default:
false
) - second-level-caching-enabled
-
Enable/Disable 2nd level cache. (default:
true
)
Database operations:
// Insert
Developer developer = new Developer();
developer.name = "Alex";
developer.persist();
// Find All
Developer.findAll().list();
// Hibernate Filters
Person.findAll().filter("Person.hasName", Parameters.with("name", "Alex"));
// Find By Query
Developer.find("name", "Alex").firstResult();
// Delete
Developer developer = new Developer();
developer.id = 1;
developer.delete();
Person.deleteById(id);
// Delete By Query
long numberOfDeleted = Developer.delete("name", "Alex");
Remember to annotate methods with @Transactional
annotation to make changes persisted in the database.
If queries start with the keyword from
then they are treated as HQL query, if not then next short form is supported:
-
order by
which expands tofrom EntityName order by …
-
<columnName>
which expands tofrom EntityName where <columnName>=?
-
<query>
which is expanded tofrom EntityName where <query>
Static Methods
findById
:Object
-
Returns object or null if not found. Overloaded version with
LockModeType
is provided.
findByIdOptional
:Optional<Object>
-
Returns object or
java.util.Optional
. find
:String
, [Object…
,Map<String, Object>
,Parameters
]-
Lists of entities meeting given query with parameters set. Returning a
PanacheQuery
. find
:String
,Sort
, [Object…
,Map<String, Object>
,Parameters
]-
Lists of entities meeting given query with parameters set sorted by
Sort
attribute/s. Returning aPanacheQuery
. findAll
-
Finds all entities. Returning a
PanacheQuery
. findAll
:Sort
-
Finds all entities sorted by
Sort
attribute/s. Returning aPanacheQuery
. list
:String
, [Object…
,Map<String, Object>
,Parameters
]-
Lists of entities meeting given query with parameters set. Returning a
List
. list
:String
,Sort
, [Object…
,Map<String, Object>
,Parameters
]-
Lists of entities meeting given query with parameters set sorted by
Sort
attribute/s. Returning aList
. listAll
-
Finds all entities. Returning a
List
. listAll
:Sort
-
Finds all entities sorted by
Sort
attribute/s. Returning aList
. stream
:String
, [Object…
,Map<String, Object>
,Parameters
]-
java.util.stream.Stream
of entities meeting given query with parameters set. stream
:String
,Sort
, [Object…
,Map<String, Object>
,Parameters
]-
java.util.stream.Stream
of entities meeting given query with parameters set sorted bySort
attribute/s. streamAll
-
java.util.stream.Stream
of all entities. streamAll
:Sort
-
java.util.stream.Stream
of all entities sorted bySort
attribute/s. count
-
Number of entities.
count
:String
, [Object…
,Map<String, Object>
,Parameters
]-
Number of entities meeting given query with parameters set.
Enables a Hibernate filter during fetching of results for this query.
deleteAll
-
Number of deleted entities.
delete
:String
, [Object…
,Map<String, Object>
,Parameters
]-
Number of deleted entities meeting given query with parameters set.
deleteById
:boolean
, [Object
]-
Delete by id. Returns if deleted or not.
persist
: [Iterable
,Steram
,Object…
]-
Persist object.
In case of using streams, remember to close them or use a try/catch
block: try (Stream<Person> persons = Person.streamAll())
.
find methods defines a withLock(LockModeType) to define the lock type and withHint(QueryHints.HINT_CACHEABLE, "true") to define hints.
|
Named Queries
@Entity
@NamedQuery(name = "Person.getByName", query = "from Person where name = :name")
public class Person extends PanacheEntity {
public static Person findByName(String name){
return find("#Person.getByName", name).firstResult();
}
}
Pagination
PanacheQuery<Person> livingPersons = Person
.find("status", Status.Alive);
livingPersons.page(Page.ofSize(25));
// get the first page
List<Person> firstPage = livingPersons.list();
// get the second page
List<Person> secondPage = livingPersons.nextPage().list();
Range
PanacheQuery<Person> livingPersons = Person
.find("status", Status.Alive);
List<Person> secondRange = livingPersons.range(25, 49).list();
You cannot mix pagination and range.
If entities are defined in external JAR, you need to enable in these projects the Jandex
plugin in project.
<plugin>
<groupId>org.jboss.jandex</groupId>
<artifactId>jandex-maven-plugin</artifactId>
<version>1.0.3</version>
<executions>
<execution>
<id>make-index</id>
<goals>
<goal>jandex</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.jboss</groupId>
<artifactId>jandex</artifactId>
<version>2.1.1.Final</version>
</dependency>
</dependencies>
</plugin>
Panache includes an annotation processor that enhance your entities.
If you disable annotation processors you might need to create a marker file on Panache archives at META-INF/panache-archive.marker
manually.
Testing
To mock using active record pattern:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-panache-mock</artifactId>
<scope>test</scope>
</dependency>
@Test
public void testPanacheMocking() {
PanacheMock.mock(Person.class);
Mockito.when(Person.count()).thenReturn(23l);
Assertions.assertEquals(23, Person.count());
PanacheMock.verify(Person.class, Mockito.times(1)).count();
}
DevServices
When testing or running in dev mode Quarkus can even provide you with a zero config database out of the box. Depending on your database type you may need docker installed in order to use this feature.
The following open source databases:
-
Postgresql (container)
-
MySQL (container)
-
MariaDB (container)
-
H2 (in-process)
-
Apache Derby (in-process)
To use DevServices don’t configure a database URL, username and password, Quarkus will provide the database and you can just start coding without worrying about config.
quarkus.datasource.db-kind=mariadb
%prod.quarkus.datasource.jdbc.url=jdbc:mariadb://db:3306/mydb
%prod.quarkus.datasource.username=developer
%prod.quarkus.datasource.password=developer
Possible configuration values prefixed with quarkus.datasource
:
devservices
-
If devservices is enabled or not. (default:
true
) devservices.image-name
-
The container image name to use instead of the default one.
devservices.properties
-
Generic properties that are added to the database connection URL.
DAO pattern
Also supports DAO pattern with PanacheRepository<TYPE>
.
@ApplicationScoped
public class DeveloperRepository
implements PanacheRepository<Person> {
public Person findByName(String name){
return find("name", name).firstResult();
}
}
EntityManager
You can inject EntityManager
in your classes:
@Inject
EntityManager em;
@Inject
org.hibernate.Session session;
@Inject
org.hibernate.SessionFactory sessionFactory;
em.persist(car);
Multiple datasources
You can register more than one datasource.
# default
quarkus.datasource.db-kind=h2
quarkus.datasource.jdbc.url=jdbc:h2:tcp://localhost/mem:default
....
# users datasource
quarkus.datasource.users.devservices=false
quarkus.datasource.users.db-kind=h2
quarkus.datasource.users.jdbc.url=jdbc:h2:tcp://localhost/mem:users
Notice that after datasource
you set the datasource name, in previous case users
.
You can inject then AgroalDataSource
with io.quarkus.agroal.DataSource
.
@DataSource("users")
AgroalDataSource dataSource1;
Flushing
You can force flush operation by calling .flush()
or .persistAndFlush()
to make it in a single call.
This flush is less efficient and you still need to commit transaction. |
Testing
There is a Quarkus Test Resource that starts and stops H2 server before and after test suite.
Register dependency io.quarkus:quarkus-test-h2:test
.
And annotate the test:
@QuarkusTestResource(H2DatabaseTestResource.class)
public class FlywayTestResources {
}
Transactions
The easiest way to define your transaction boundaries is to use the @Transactional
annotation.
Transactions are mandatory in case of none idempotent operations.
@Transactional
public void createDeveloper() {}
You can control the transaction scope:
-
@Transactional(REQUIRED)
(default): starts a transaction if none was started, stays with the existing one otherwise. -
@Transactional(REQUIRES_NEW)
: starts a transaction if none was started; if an existing one was started, suspends it and starts a new one for the boundary of that method. -
@Transactional(MANDATORY)
: fails if no transaction was started ; works within the existing transaction otherwise. -
@Transactional(SUPPORTS)
: if a transaction was started, joins it ; otherwise works with no transaction. -
@Transactional(NOT_SUPPORTED)
: if a transaction was started, suspends it and works with no transaction for the boundary of the method; otherwise works with no transaction. -
@Transactional(NEVER)
: if a transaction was started, raises an exception; otherwise works with no transaction.
You can configure the default transaction timeout using quarkus.transaction-manager.default-transaction-timeout
configuration property. By default it is set to 60 seconds.
You can set a timeout property, in seconds, that applies to transactions created within the annotated method by using @TransactionConfiguration
annotation.
@Transactional
@TransactionConfiguration(timeout=40)
public void createDeveloper() {}
If you want more control over transactions you can inject UserTransaction
and use a programmatic way.
@Inject UserTransaction transaction
transaction.begin();
transaction.commit();
transaction.rollback();
You can implement your custom credentials provider (ie Azure KeyVault) to provide a username/password for the database connection. Name
information is not necessary if there is only one custom credential provider.
@ApplicationScoped
@Unremovable
@Named("my-credentials-provider")
public class CustomCredentialsProvider implements CredentialsProvider {
@Inject
Config config;
@Override
public Properties getCredentials(String credentialsProviderName) {
properties.put(CredentialsProvider.USER_PROPERTY_NAME, "hibernate_orm_test");
properties.put(CredentialsProvider.PASSWORD_PROPERTY_NAME, "hibernate_orm_test");
}
}
quarkus.datasource.credentials-provider=
custom
quarkus.datasource.credentials-provider-name=
my-credentials-provider
Hibernate Multitenancy
Multitenancy is supported using Schema or Database approach. First you need to define how tenant is identified:
@RequestScoped
@Unremovable
public class CustomTenantResolver implements TenantResolver {
@Inject
RoutingContext context;
@Override
public String getDefaultTenantId() {
return "base";
}
@Override
public String resolveTenantId() {
}
}
Schema approach
quarkus.hibernate-orm.database.generation=none
quarkus.hibernate-orm.multitenant=SCHEMA
Database approach
quarkus.hibernate-orm.database.generation=none
quarkus.hibernate-orm.multitenant=DATABASE
# default tenant
quarkus.datasource.base.db-kind=postgresql
quarkus.datasource.base.username=quarkus_test
...
# Tenant 'mycompany'
quarkus.datasource.mycompany.db-kind=postgresql
quarkus.datasource.mycompany.username=mycompany
quarkus.flyway.mycompany.locations=classpath:database/mycompany
...
If you need more dynamic approach implement: @ApplicationScoped io.quarkus.hibernate.orm.runtime.tenant.TenantConnectionResolver
Hibernate Envers
Quarkus supports Hibernate Envers.
./mvnw quarkus:add-extension
-Dextensions="hibernate-envers"
Configuration properties prefixed with quarkus.hibernate-envers
:
store-data-at-delete
-
Enable store_data_at_delete feature. (default:
false
) audit-table-suffix
-
Defines a suffix for historical data table. (default:
_AUD
) audit-table-prefix
-
Defines a prefix for historical data table. (default: ``)
revision-field-name
-
Revision field name. (default:
REV
) revision-type-field-name
-
Revision type field name. (default:
REVTYPE
)
REST Data Panache
REST Data with Panache extension can generate the basic CRUD endpoints for your entities and repositories.
./mvnw quarkus:add-extension
-Dextensions="hibernate-orm-rest-data-panache"
You also need to add the JDBC driver extension and a JSON Marshaller (ie resteasy-jackson
).
Then you can define interfaces for defining endpoints:
In case of Active Record pattern:
public interface DeveloperResource extends PanacheEntityResource<Developer, Long> {
}
In case of Repository:
public interface DeveloperResource extends PanacheRepositoryResource<DeveloperRepository, Developer, Long> {
}
Quarkus will generate automatically the implementation for you following the next rules:
-
Default path is a hyphenated lowercase resource name without a suffix of
resource
orcontroller
. -
get(@PathParam("id"))
,list
,add(Developer)
,update(@PathParam("id"), Developer)
,delete(@PathParam("id"))
You can customize these defaults by using @ResourceProperties
and @MethodProperties
annotations.
@ResourceProperties(hal = true, path = "my-developer", halCollectionName = "dev-collections")
public interface DeveloperResource extends PanacheEntityResource<Developer, Long> {
@MethodProperties(path = "all")
List<Developer> list();
@MethodProperties(exposed = false)
void delete(Long id);
}
If hal
is true
, you need to send the Accept: application/hal+json
HTTP header to get the response.
Hibernate Reactive
./mvnw quarkus:add-extension
-Dextensions="quarkus-hibernate-reactive, quarkus-resteasy-mutiny, "
Also you need to add the reactive driver (ie quarkus-reactive-pg-client
).
You can use: org.hibernate.reactive.mutiny.Mutiny
or org.hibernate.reactive.stage.Stage
.
@Entity
@Table(name = "dev")
public class Developer {
}
@Inject
CompletionStage<Stage.Session> stageSession;
@Inject
Uni<Mutiny.Session> mutinySession;
public Uni<Long> reactivePersist() {
return mutinySession
.flatMap(s -> s.persist(new Developer(1, "Alex"))
.flatMap(v -> session.flush())
....
}
public CompletionStage<Developer> reactiveFind() {
return stageSession
.thenCompose(session -> {
session.find(Developer.class, 1);
});
}
Infinispan
Quarkus integrates with Infinispan:
./mvnw quarkus:add-extension
-Dextensions="infinispan-client"
Serialization uses a library called Protostream.
Annotation based
@ProtoFactory
public Author(String name, String surname) {
this.name = name;
this.surname = surname;
}
@ProtoField(number = 1)
public String getName() {
return name;
}
@ProtoField(number = 2)
public String getSurname() {
return surname;
}
Initializer to set configuration settings.
@AutoProtoSchemaBuilder(includeClasses =
{ Book.class, Author.class },
schemaPackageName = "book_sample")
interface BookContextInitializer
extends SerializationContextInitializer {
}
User written based
There are three ways to create your schema:
Protofile
Creates a .proto
file in the META-INF
directory.
package book_sample;
message Author {
required string name = 1;
required string surname = 2;
}
In case of having a Collection field you need to use the repeated
key (ie repeated Author authors = 4
).
In code
Setting proto
schema directly in a produced bean.
@Produces
FileDescriptorSource bookProtoDefinition() {
return FileDescriptorSource
.fromString("library.proto",
"package book_sample;\n" +
"message Author {\n" +
" required string name = 1;\n" +
" required string surname = 2;\n" +
"}");
}
Marshaller
Using org.infinispan.protostream.MessageMarshaller
interface.
public class AuthorMarshaller
implements MessageMarshaller<Author> {
@Override
public String getTypeName() {
return "book_sample.Author";
}
@Override
public Class<? extends Author> getJavaClass() {
return Author.class;
}
@Override
public void writeTo(ProtoStreamWriter writer,
Author author) throws IOException {
writer.writeString("name", author.getName());
writer.writeString("surname", author.getSurname());
}
@Override
public Author readFrom(ProtoStreamReader reader)
throws IOException {
String name = reader.readString("name");
String surname = reader.readString("surname");
return new Author(name, surname);
}
}
And producing the marshaller:
@Produces
MessageMarshaller authorMarshaller() {
return new AuthorMarshaller();
}
Infinispan Embedded
./mvnw quarkus:add-extension
-Dextensions="infinispan-embeddedy"
Configuration in infinispan.xml
:
<local-cache name="quarkus-transaction">
<transaction
transaction-manager-lookup=
"org.infinispan.transaction.lookup.JBossStandaloneJTAManagerLookup"/>
</local-cache>
Set configuration file location in application.properties
:
quarkus.infinispan-embedded.xml-config=infinispan.xml
And you can inject the main entry point for the cache:
@Inject
org.infinispan.manager.EmbeddedCacheManager cacheManager;
DevServices
When testing or running in dev mode Quarkus can even provide you with a zero config Infinispan out of the box.
Possible configuration values prefixed with quarkus.infinispan-client
:
devservices.enabled
-
If devservices is enabled or not. (default:
true
) devservices.port
-
Optional fixed port the dev service will listen to. If not defined, the port will be chosen randomly.
devservices.shared
-
Indicates if the Infinispan managed by Quarkus Dev Services is shared. When shared, Quarkus looks for running containers using label-based service discovery. (default:
true
) devservices.service-name
-
The value of the
quarkus-dev-service-infinispan
label attached to the started container. (default:infinispan
) devservices.artifacts
-
List of the artifacts to automatically download and add to the Infinispan server libraries.
Redis
Quarkus integrates with Redis.
./mvnw quarkus:add-extension
-Dextensions="redis-client"
Configure Redis location:
quarkus.redis.hosts=localhost:6379
You can use synchronous or reactive clients:
@Inject
RedisClient redisClient;
@Inject
ReactiveRedisClient reactiveRedisClient;
void increment(String key, Integer incrementBy) {
redisClient.incrby(key, incrementBy.toString());
}
Uni<List<String>> keys() {
return reactiveRedisClient
.keys("*")
.map(response -> {
List<String> result = new ArrayList<>();
for (Response r : response) {
result.add(r.toString());
}
return result;
});
}
DevServices
When testing or running in dev mode Quarkus can even provide you with a zero config Redis out of the box.
Possible configuration values prefixed with quarkus.redis
:
devservices.enabled
-
If devservices is enabled or not. (default:
true
) devservices.image-name
-
The container image name to use instead of the default one.
devservices.port
-
Optional fixed port the dev service will listen to. If not defined, the port will be chosen randomly.
devservices.shared
-
Indicates if the Redis broker managed by Quarkus Dev Services is shared. When shared, Quarkus looks for running containers using label-based service discovery. (default:
true
) devservices.service-name
-
The value of the
quarkus-dev-service-redis
label attached to the started container. (default:redis
)
Multiple Redis Clients
quarkus.redis.hosts = localhost:6379
quarkus.redis.second.hosts = localhost:6379
@Inject
RedisClient defaultRedisClient;
@Inject
@RedisClientName("second")
RedisClient redisClient2;
List of Redis parameters.
quarkus.redis
as prefix is skipped in the next table.
health.enabled
-
Health check is published in case the smallrye-health extension is present. (default:
true
) password
-
The Redis password.
hosts
-
The Redis hosts. (default:
localhost:6379
) database
-
The Redis database.
timeout
-
The maximum delay to wait before a blocking command to redis server times out. (default:
10s
) ssl
-
Enables or disables the SSL on connect.
clinet-type
-
The Redis client type. Possible values:
standalone
,cluster
,sentinel
(default:standalone
)
Flyway
Quarkus integrates with Flyway to help you on database schema migrations.
./mvnw quarkus:add-extension
-Dextensions="quarkus-flyway"
Then place migration files to the migrations folder (classpath:db/migration
).
You can inject org.flywaydb.core.Flyway
to programmatically execute the migration.
@Inject
Flyway flyway;
flyway.migrate();
Or can be automatically executed by setting migrate-at-start
property to true
.
quarkus.flyway.migrate-at-start=true
List of Flyway parameters.
quarkus.flyway
as prefix is skipped in the next table.
clean-at-start
-
Execute Flyway clean command (default:
false
) migrate-at-start
-
Flyway migration automatically (default:
false
) locations
-
CSV locations to scan recursively for migrations. Supported prefixes
classpath
andfilesystem
(default:classpath:db/migration
). connect-retries
-
The maximum number of retries when attempting to connect (default: 0)
schemas
-
CSV case-sensitive list of schemas managed (default: none)
table
-
The name of Flyway’s schema history table (default:
flyway_schema_history
) out-of-order
-
Allows migrations to be run "out of order".
ignore-missing-migrations
-
Ignore missing migrations when reading the history table.
sql-migration-prefix
-
Prefix for versioned SQL migrations (default:
V
)
repeatable-sql-migration-prefix::
Prefix for repeatable SQL migrations (default: R
)
baseline-on-migrate
-
Only migrations above baseline-version will then be applied
baseline-version
-
Version to tag an existing schema with when executing baseline (default: 1)
baseline-description
-
Description to tag an existing schema with when executing baseline (default:
Flyway Baseline
)
validate-on-migrate
-
Validate the applied migrations against the available ones (default:
true
)
placeholder-prefix
-
Prefix of every placeholder (default:
${
) placeholder-suffix
-
Suffix of every placeholder (default:
}
)
callbacks
-
Comma-separated list of fully qualified class names of Callback implementations.
ignore-future-migrations
-
Ignore future migrations when reading the history table.
Multiple Datasources
To use multiple datasource in Flyway you just need to add the datasource name just after the flyway
property:
quarkus.datasource.users.jdbc.url=jdbc:h2:tcp://localhost/mem:users
quarkus.datasource.inventory.jdbc.url=jdbc:h2:tcp://localhost/mem:inventory
# ...
quarkus.flyway.users.schemas=USERS_TEST_SCHEMA
quarkus.flyway.inventory.schemas=INVENTORY_TEST_SCHEMA
# ...
Liquibase
Quarkus integrates with Liquibase to help you on database schema migrations.
./mvnw quarkus:add-extension
-Dextensions="quarkus-liquibase"
Then place changelog files to the (src/main/resources/db
) folder.
You can inject org.quarkus.liquibase.LiquibaseFactory
to programmatically execute the migration.
@Inject
LiquibaseFactory liquibaseFactory;
try (Liquibase liquibase = liquibaseFactory.createLiquibase()) {
...
}
Or can be automatically executed by setting migrate-at-start
property to true
.
quarkus.liquibase.migrate-at-start=true
List of Liquibase parameters.
quarkus.liquibase
as prefix is skipped in the next table.
- change-log
-
The change log file.
XML
,YAML
,JSON
,SQL
formats supported. (default:db/changeLog.xml
) - change-log-parameters."<parameter-name>"
-
Liquibase changelog parameters.
- migrate-at-start
-
The migrate at start flag. (default:
false
) - validate-on-migrate
-
The validate on update flag. (default:
false
) - clean-at-start
-
The clean at start flag. (default:
false
) - contexts
-
The list of contexts.
- labels
-
The list of labels.
- database-change-log-table-name
-
The database change log lock table name. (default:
DATABASECHANGELOG
) - database-change-log-lock-table-name
-
The database change log lock table name. (default:
DATABASECHANGELOGLOCK
) - default-catalog-name
-
The default catalog name.
- default-schema-name
-
The default schema name.
- liquibase-catalog-name
-
The liquibase tables catalog name.
- liquibase-schema-name
-
The liquibase tables schema name.
- liquibase-tablespace-name
-
The liquibase tables tablespace name.
Multiple Datasources
To use multiple datasource in Liquibase you just need to add the datasource name just after the liquibase
property:
quarkus.datasource.users.jdbc.url=jdbc:h2:tcp://localhost/mem:users
quarkus.datasource.inventory.jdbc.url=jdbc:h2:tcp://localhost/mem:inventory
# ...
quarkus.liquibase.users.schemas=USERS_TEST_SCHEMA
quarkus.liquibase.inventory.schemas=INVENTORY_TEST_SCHEMA
# ...
Liquibase MongoDB
./mvnw quarkus:add-extension
-Dextensions="liquibase-mongodb"
Most of the configuration properties are valid but changing prefix to: quarkus.liquibase-mongodb
Hibernate Search
Quarkus integrates with Elasticsearch to provide a full-featured full-text search using Hibernate Search API.
./mvnw quarkus:add-extension
-Dextensions="quarkus-hibernate-search-elasticsearch"
You need to annotate your model with Hibernate Search API to index it:
@Entity
@Indexed
public class Author extends PanacheEntity {
@FullTextField(analyzer = "english")
public String bio;
@FullTextField(analyzer = "name")
@KeywordField(name = "firstName_sort",
sortable = Sortable.YES,
normalizer = "sort")
public String firstName;
@OneToMany
@IndexedEmbedded
public List<Book> books;
}
It is not mandatory to use Panache. |
You need to define the analyzers and normalizers defined in annotations.
You only need to implement ElasticsearchAnalysisConfigurer
interface and configure it.
public class MyQuarkusAnalysisConfigurer
implements ElasticsearchAnalysisConfigurer {
@Override
public void configure(
ElasticsearchAnalysisDefinitionContainerContext ctx)
{
ctx.analyzer("english").custom()
.withTokenizer("standard")
.withTokenFilters("asciifolding",
"lowercase", "porter_stem");
ctx.normalizer("sort").custom()
.withTokenFilters("asciifolding", "lowercase");
}
}
Use Hibernate Search in REST service:
public class LibraryResource {
@Inject
EntityManager em;
@Transactional
public List<Author> searchAuthors(
@QueryParam("pattern") String pattern) {
return Search.getSearchSession(em)
.search(Author.class)
.predicate(f ->
pattern == null || pattern.isEmpty() ?
f.matchAll() :
f.simpleQueryString()
.onFields("firstName",
"lastName", "books.title")
.matching(pattern)
)
.sort(f -> f.byField("lastName_sort")
.then().byField("firstName_sort"))
.fetchHits();
}
When not using Hibernate ORM, index data using Search.getSearchSession(em).createIndexer() .startAndWait() at startup time.
|
Configure the extension in application.properties
:
quarkus.hibernate-search.elasticsearch.version=7
quarkus.hibernate-search.elasticsearch.
analysis-configurer=MyQuarkusAnalysisConfigurer
quarkus.hibernate-search.elasticsearch.
automatic-indexing.synchronization-strategy=searchable
quarkus.hibernate-search.elasticsearch.
index-defaults.lifecycle.strategy=drop-and-create
quarkus.hibernate-search.elasticsearch.
index-defaults.lifecycle.required-status=yellow
List of Hibernate-Elasticsearch properties prefixed with quarkus.hibernate-search.elasticsearch
:
backends
-
Map of configuration of additional backends.
version
-
Version of Elasticsearch
analysis-configurer
-
Class or name of the neab used to configure.
hosts
-
List of Elasticsearch servers hosts.
enabled
-
Whether Hibernate Search is enabled. (default:
true
) username
-
Username for auth.
password
-
Password for auth.
connection-timeout
-
Duration of connection timeout.
max-connections
-
Max number of connections to servers.
max-connections-per-route
-
Max number of connections to server.
indexes
-
Per-index specific configuration.
discovery.enabled
-
Enables automatic discovery.
discovery.refresh-interval
-
Refresh interval of node list.
discovery.default-scheme
-
Scheme to be used for the new nodes.
automatic-indexing.synchronization-strategy
-
Status for which you wait before considering the operation completed (
queued
,committed
orsearchable
). automatic-indexing.enable-dirty-check
-
When enabled, re-indexing of is skipped if the changes are on properties that are not used when indexing.
index-defaults.lifecycle.strategy
-
Index lifecycle (
none
,validate
,update
,create
,drop-and-create
,drop-abd-create-drop
) index-defaults.lifecycle.required-status
-
Minimal cluster status (
green
,yellow
,red
) index-defaults.lifecycle.required-status-wait-timeout
-
Waiting time before failing the bootstrap.
index-defaults.refresh-after-write
-
Set if index should be refreshed after writes.
Possible annotations:
@Indexed
-
Register entity as full text index
@FullTextField
-
Full text search. Need to set an analyzer to split tokens.
@KeywordField
-
The string is kept as one single token but can be normalized.
IndexedEmbedded
-
Include the Book fields into the Author index.
@ContainerExtraction
-
Sets how to extract a value from container, e.g from a
Map
. @DocumentId
-
Map an unusual entity identifier to a document identifier.
@GenericField
-
Full text index for any supported type.
@IdentifierBridgeRef
-
Reference to the identifier bridge to use for a
@DocumentId
. @IndexingDependency
-
How a dependency of the indexing process to a property should affect automatic reindexing.
@ObjectPath
@ScaledNumberField
-
For
java.math.BigDecimal
orjava.math.BigInteger
that you need higher precision.
If you are using ElasticSearch in AWS you might need some extra config provided by an special extension https://quarkiverse.github.io/quarkiverse-docs/quarkus-hibernate-search-extras/dev/index.html
Amazon DynamoDB
Quarkus integrates with https://aws.amazon.com/dynamodb/:
./mvnw quarkus:add-extension
-Dextensions="quarkus-amazon-dynamodb"
@Inject
DynamoDbClient dynamoDB;
To use asycnhronous client with Mutiny:
./mvnw quarkus:add-extension
-Dextensions="quarkus-amazon-dynamodb, resteasy-mutiny"
@Inject
DynamoDbAsyncClient dynamoDB;
Uni.createFrom().completionStage(() -> dynamoDB.scan(scanRequest()))....
To use it as a local DynamoDB instance:
quarkus.dynamodb.region=
eu-central-1
quarkus.dynamodb.endpoint-override=
http://localhost:8000
quarkus.dynamodb.credentials.type=STATIC
quarkus.dynamodb.credentials.static-provider
.access-key-id=test-key
quarkus.dynamodb.credentials.static-provider
.secret-access-key=test-secret
If you want to work with an AWS account, you’d need to set it with:
quarkus.dynamodb.region=<YOUR_REGION>
quarkus.dynamodb.credentials.type=DEFAULT
DEFAULT
credentials provider chain:
-
System properties
aws.accessKeyId
,aws.secretKey
-
Env. Varables
AWS_ACCESS_KEY_ID
,AWS_SECRET_ACCESS_KEY
-
Credentials profile
~/.aws/credentials
-
Credentials through the Amazon EC2 container service if the
AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
set -
Credentials through Amazon EC2 metadata service.
Configuration parameters prefixed with quarkus.dynamodb
:
Parameter |
Default |
Description |
|
|
Endpoint discovery for a service API that supports endpoint discovery. |
|
Configure the endpoint with which the SDK should communicate. |
|
|
Time to complete an execution. |
|
|
List of class interceptors. |
Configuration parameters prefixed with quarkus.dynamodb.aws
:
Parameter | Default | Description |
---|---|---|
|
Region that hosts DynamoDB. |
|
|
|
Credentials that should be used |
Credentials specific parameters prefixed with quarkus.dynamodb.aws.credentials
:
Parameter | Default | Description |
---|---|---|
DEFAULT |
||
|
|
Should fetch credentials async. |
|
|
Should reuse the last successful credentials. |
STATIC |
||
|
AWS access key id. |
|
|
AWS secret access key. |
|
PROFILE |
||
|
|
The name of the profile to use. |
PROCESS |
||
|
Command to execute to retrieve credentials. |
|
|
1024 |
Max bytes to retrieve from process. |
|
|
The amount of time between credentials expire and credentials refreshed. |
|
|
Should fetch credentials async. |
In case of synchronous client, the next parameters can be configured prefixed by quarkus.dynamodb.sync-client
:
Parameter | Default | Description |
---|---|---|
|
|
Connection acquisation timeout. |
|
|
Max time to connection to be opened. |
|
Connection timeout. |
|
|
|
Max time connection to be open. |
|
|
Time to wait for data. |
|
|
Max connections. |
|
|
Client send an HTTP |
|
|
Connections in pool should be closed asynchronously. |
|
Endpoint of the proxy server. |
|
|
|
Enables HTTP proxy. |
|
Proxy username. |
|
|
Proxy password. |
|
|
For NTLM, domain name. |
|
|
For NTLM, workstation name. |
|
|
Authenticate pre-emptively. |
|
|
List of non proxy hosts. |
|
|
|
TLS manager: |
|
Path to key store. |
|
|
Key store type. |
|
|
Key store password. |
In case of asynchronous client, the next parameters can be configured prefixed by quarkus.dynamodb.async-client
:
Parameter | Default | Description |
---|---|---|
|
|
Connection acquisation timeout. |
|
|
Max time to connection to be opened. |
|
Connection timeout. |
|
|
|
Max time connection to be open. |
|
|
Max number of concurrent connections. |
|
|
Connections in pool should be closed asynchronously. |
|
|
Read timeout. |
|
|
Write timeout. |
|
Endpoint of the proxy server. |
|
|
|
Enables HTTP proxy. |
|
List of non proxy hosts. |
|
|
|
TLS manager: |
|
Path to key store. |
|
|
Key store type. |
|
|
Key store password. |
|
|
SSL Provider ( |
|
|
|
Sets the HTTP protocol. |
|
Max number of concurrent streams. |
|
|
|
Enable custom event loop conf. |
|
Number of threads to use in event loop. |
|
|
|
Prefix of thread names. |
Amazon S3
./mvnw quarkus:add-extension
-Dextensions="quarkus-amazon-s3"
@Inject
S3Client s3Client;
You need to set a HTTP client either URL Connection
:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>url-connection-client</artifactId>
</dependency>
or Apache HTTP:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>apache-client</artifactId>
</dependency>
quarkus.s3.sync-client.type=apache
And configure it:
quarkus.s3.endpoint-override=http://localhost:8008
quarkus.s3.interceptors=io.quarkus.it.amazon.s3.S3ModifyResponse
quarkus.s3.aws.region=us-east-1
quarkus.s3.aws.credentials.type=static
quarkus.s3.aws.credentials.static-provider.access-key-id=test-key
quarkus.s3.aws.credentials.static-provider.secret-access-key=test-secret
You can inject asynchronous client too:
@Inject
S3AsyncClient s3AsyncClient;
And you need to add the asynchronous Netty client:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>netty-nio-client</artifactId>
</dependency>
Configuration properties are the same as Amazon DynamoDB but changing the prefix from dynamodb
to s3
.
Neo4j
Quarkus integrates with Neo4j:
./mvnw quarkus:add-extension
-Dextensions="quarkus-neo4j"
@Inject
org.neo4j.driver.Driver driver;
Configuration properties:
quarkus.neo4j
as prefix is skipped in the next table.
Prefix is quarkus.neo4j
.
uri
-
URI of Neo4j. (default:
localhost:7687
) authentication.username
-
Username. (default:
neo4j
) authentication.password
-
Password. (default:
neo4j
) authentication.disabled
-
Disable authentication. (default:
false
) pool.metrics-enabled
-
Enable metrics. (default:
false
) pool.log-leaked-sessions
-
Enable leaked sessions logging. (default:`false`)
pool.max-connection-pool-size
-
Max amount of connections. (default:
100
) pool.max-connection-lifetime
-
Pooled connections older will be closed and removed from the pool. (default:
1H
) pool.connection-acquisition-timeout
-
Timout for connection adquisation. (default: 1M)
pool.idle-time-before-connection-test
-
Pooled connections idled in the pool for longer than this timeout will be tested before they are used. (default:
-1
)
As Neo4j uses SSL communication by default, to create a native executable you need to compile with next options GraalVM options:
-H:EnableURLProtocols=http,https --enable-all-security-services -H:+JNI
And Quarkus Maven Plugin with next configuration:
<artifactId>quarkus-maven-plugin</artifactId>
<executions>
<execution>
<id>native-image</id>
<goals>
<goal>native-image</goal>
</goals>
<configuration>
<enableHttpUrlHandler>true
</enableHttpUrlHandler>
<enableHttpsUrlHandler>true
</enableHttpsUrlHandler>
<enableAllSecurityServices>true
</enableAllSecurityServices>
<enableJni>true</enableJni>
</configuration>
</execution>
</executions>
Alternatively, and as a not recommended way in production, you can disable SSL and Quarkus will disable Bolt SSL as well. quarkus.ssl.native=false
.
If you are using Neo4j 4.0, you can use fully reactive.
Add the next extension: quarkus-resteasy-mutiny
.
@GET
public Publisher<String> get() {
return Multi.createFrom().resource(driver::rxSession,
session -> session.readTransaction(tx -> {
RxResult result = tx.run("MATCH (f:Fruit) RETURN f.name as name");
return Multi.createFrom().publisher(result.records())
.map(record -> record.get("name").asString());
})
).withFinalizer(session -> {
return Uni.createFrom().publisher(session.close());
});
}
DevServices
When testing or running in dev mode Quarkus can even provide you with a zero config Neo4j out of the box.
Possible configuration values are shown at Neo4J
MongoDB Client
Quarkus integrates with MongoDB:
./mvnw quarkus:add-extension
-Dextensions="quarkus-mongodb-client"
@Inject
com.mongodb.client.MongoClient client;
@Inject
io.quarkus.mongodb.reactive.ReactiveMongoClient client;
INFO: Reactive client uses exposes Mutiny API.
quarkus.mongodb.connection-string=mongodb://localhost:27018
quarkus.mongodb.write-concern.journal=false
Testing
To start an embedded MongoDB database for your unit tests, Quarkus provides two QuarkusTestResourceLifecycleManager
.
Add io.quarkus:quarkus-test-mongodb
dependency.
// Single MongoDB instance
@QuarkusTestResource(io.quarkus.test.mongodb.MongoTestResource.class)
// ReplicaSet with two instances
@QuarkusTestResource(io.quarkus.test.mongodb.MongoReplicaSetTestResource.class)
Multi MongoDB support
You can configure multiple MongoDB clients using same approach as with DataSource
.
The syntax is quarkus.mongodb.<optional name>.<property>
:
quarkus.mongodb.users.connection-string = mongodb://mongo2:27017/userdb
quarkus.mongodb.inventory.connection-string = mongodb://mongo3:27017/invdb
Inject the instance using @io.quarkus.mongodb.runtime.MongoClientName
annotation:
@Inject
@MongoClientName("users")
MongoClient mongoClient1;
DevServices
When testing or running in dev mode Quarkus can even provide you with a zero config MongoDB database out of the box.
%prod.quarkus.mongodb.connection-string=mongodb://localhost:27018
Possible configuration values prefixed with quarkus.datasource
:
devservices.enabled
-
If devservices is enabled or not. (default:
true
) devservices.image-name
-
The container image name to use instead of the default one.
devservices.port
-
Optional fixed port the dev service will listen to. If not defined, the port will be chosen randomly.
devservices.properties
-
Generic properties that are added to the database connection URL.
quarkus.mongodb
as prefix is skipped in the following table.
Parameter | Type | Description |
---|---|---|
|
|
MongoDB connection URI. |
|
|
Addresses passed as |
|
|
Application name. |
|
|
Maximum number of connections. |
|
|
Minimum number of connections. |
|
|
Idle time of a pooled connection. |
|
|
Life time of pooled connection. |
|
|
Maximum wait time for new connection. |
|
|
Time period between runs of maintenance job. |
|
|
Time to wait before running the first maintenance job. |
|
|
Multiplied with |
|
|
|
|
|
|
|
|
Insecure TLS. |
|
|
Enable TLS |
|
|
Implies hosts given are a seed list. |
|
|
Time to wait for server selection. |
|
|
Minimum ping time to make a server eligible. |
|
|
Frequency to determine the state of servers. |
|
|
Read preferences. |
|
|
Max number of concurrent operations allowed to wait. |
|
|
Ensures are writes are ack. |
|
|
Journal writing aspect. |
|
|
Value to all write commands. |
|
|
Retry writes if network fails. |
|
|
Timeout to all write commands. |
|
|
Username. |
|
|
Password. |
|
|
|
|
|
Source of the authentication credentials. |
|
|
Authentication mechanism properties. |
|
|
Configures the read concern. Possible values: |
MongoDB Panache
You can also use the Panache framework to write persistence part when using MongoDB.
./mvnw quarkus:add-extension
-Dextensions="mongodb-panache"
MongoDB configuration comes from MongoDB Client section.
@MongoEntity(collection="ThePerson")
public class Person extends PanacheMongoEntity {
public String name;
@BsonProperty("birth")
public LocalDate birthDate;
public Status status;
}
Possible annotations in fields: @BsonId
(for custom ID), @BsonProperty
and @BsonIgnore
.
@MongoEntity is optional.
|
Multi-tenancy with MongoDB Panache
@MongoEntity(collection = "TheBook", clientName = "client2", database = "database2")
Methods provided are similar of the ones shown in Persistence section.
person.persist();
person.update();
person.delete();
List<Person> allPersons = Person.listAll();
person = Person.findById(personId);
List<Person> livingPersons = Person.list("status", Status.Alive);
List<Person> persons = Person.list(Sort.by("name").and("birth"));
long updated = Person.update("name", "Mortal").where("status", Status.Alive);
long countAll = Person.count();
Person.deleteById(id);
Person.delete("status", Status.Alive);
All list
methods have equivalent stream
versions.
Pagination
You can also use pagination:
PanacheQuery<Person> livingPersons =
Person.find("status", Status.Alive);
livingPersons.page(Page.ofSize(25));
// get the first page
List<Person> firstPage = livingPersons.list();
// get the second page
List<Person> secondPage = livingPersons.nextPage().list();
Range
PanacheQuery<Person> livingPersons = Person
.find("status", Status.Alive);
List<Person> secondRange = livingPersons.range(25, 49).list();
You cannot mix pagination and range.
Queries
Native MongoDB queries are supported (if they start with {
or org.bson.Document
instance) as well as Panache Queries.
Panache Queries equivalence in MongoDB:
-
firstname = ?1 and status = ?2
→{'firstname': ?1, 'status': ?2}
-
amount > ?1 and firstname != ?2
→{'amount': {'$gt': ?1}, 'firstname': {'$ne': ?2}}
-
lastname like ?1
→{'lastname': {'$regex': ?1}}
-
lastname is not null
→{'lastname':{'$exists': true}}
PanacheQL refers to the Object parameter name but native queries refer to MongoDB field names. |
Projection
Projection can be done for both PanacheQL and native queries.
import io.quarkus.mongodb.panache.ProjectionFor;
@ProjectionFor(Person.class) (1)
public class PersonName {
public String name;
}
PanacheQuery<PersonName> shortQuery = Person.find("status ", Status.Alive).project(PersonName.class);
1 | Entity class. |
Transsactions
To use them with MongoDB with Panache you need to annotate the method that starts the transaction with the @Transactional
annotation.
Testing
To mock using active record pattern:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-panache-mock</artifactId>
<scope>test</scope>
</dependency>
@Test
public void testPanacheMocking() {
PanacheMock.mock(Person.class);
Mockito.when(Person.count()).thenReturn(23l);
Assertions.assertEquals(23, Person.count());
PanacheMock.verify(Person.class, Mockito.times(1)).count();
}
DAO pattern
@ApplicationScoped
public class PersonRepository
implements PanacheMongoRepository<Person> {
}
Jandex
If entities are defined in external JAR, you need to enable in these projects the Jandex
plugin in project.
<plugin>
<groupId>org.jboss.jandex</groupId>
<artifactId>jandex-maven-plugin</artifactId>
<version>1.0.3</version>
<executions>
<execution>
<id>make-index</id>
<goals>
<goal>jandex</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.jboss</groupId>
<artifactId>jandex</artifactId>
<version>2.1.1.Final</version>
</dependency>
</dependencies>
</plugin>
Panache includes an annotation processor that enhance your entities.
If you disable annotation processors you might need to create a marker file on Panache archives at META-INF/panache-archive.marker
manually.
Reactive Panache
MongoDB with Panache allows using reactive implementation too by using ReactivePanacheMongoEntity
or ReactivePanacheMongoEntityBase
or ReactivePanacheMongoRepository
or ReactivePanacheMongoRepositoryBase
depending on your style.
public class ReactivePerson extends ReactivePanacheMongoEntity {
public String name;
}
CompletionStage<Void> cs1 = person.persist();
CompletionStage<List<ReactivePerson>> allPersons = ReactivePerson.listAll();
Publisher<ReactivePerson> allPersons = ReactivePerson.streamAll();
Uni<List<PersonName>> persons = ReactivePersonEntity.find("lastname", name).project(PersonName.class).list();
MongoDB REST Data Panache
MongoDB REST Data with Panache extension can generate the basic CRUD endpoints for your entities and repositories.
./mvnw quarkus:add-extension
-Dextensions="mongodb-rest-data-panache"
You also need to add the JDBC driver extension and a JSON Marshaller (ie resteasy-jackson
).
Then you can define interfaces for defining endpoints:
In case of Active Record pattern:
public interface DeveloperResource extends PanacheMongoEntityResource<Developer, Long> {
}
In case of Repository:
public interface DeveloperResource extends PanacheMongoRepositoryResource<DeveloperRepository, Developer, Long> {
}
Cassandra
Quarkus integrates with Cassandra and DataStax Object Mapper.
<dependency>
<groupId>com.datastax.oss.quarkus</groupId>
<artifactId>cassandra-quarkus-client</artifactId>
</dependency>
Enities and DAOs are generated as you have been doing with DataStax Object Mapper.
You need to create a DaoProducer:
@Inject
public FruitDaoProducer(QuarkusCqlSession session) {
FruitMapper mapper = new FruitMapperBuilder(session).build();
fruitDao = mapper.fruitDao();
}
@Produces
@ApplicationScoped
FruitDao produceFruitDao() {
return fruitDao;
}
Cassandra configuration:
quarkus.cassandra.contact-points=127.0.0.1:9042
quarkus.cassandra.local-datacenter=datacenter1
quarkus.cassandra.keyspace=k1
quarkus.cassandra.auth.username=john
quarkus.cassandra.auth.password=s3cr3t
You can configure other Cassandra Java driver settings using application.conf
or application.json
files. They need to be located in the classpath of your application.
Driver settings reference.
If MicroProfile Metrics extension is registered, the Cassandra extension can provide (if enabled) metrics about the session:
quarkus.cassandra.metrics.enabled=true
quarkus.cassandra.metrics.session-enabled=connected-nodes,bytes-sent
quarkus.cassandra.metrics.node-enabled=pool.open-connections
Reactive
You can also use Mutiny to define a reactive DAO:
@Dao
public interface FruitDaoReactive {
@Update
Uni<Void> update(Fruit fruit);
@Select
MutinyMappedReactiveResultSet<Fruit> findById(String storeId);
}
@Mapper
public interface FruitMapper {
@DaoFactory
FruitDaoReactive fruitDaoReactive();
}
Reactive Programming
Quarkus implements MicroProfile Reactive spec and uses RXJava2 to provide reactive programming model.
./mvnw quarkus:add-extension
-Dextensions="quarkus-smallrye-reactive-streams-operators"
Asynchronous HTTP endpoint is implemented by returning Java CompletionStage
.
You can create this class either manually or using MicroProfile Reactive Streams spec:
@GET
@Path("/reactive")
@Produces(MediaType.TEXT_PLAIN)
public CompletionStage<String> getHello() {
return ReactiveStreams.of("h", "e", "l", "l", "o")
.map(String::toUpperCase)
.toList()
.run()
.thenApply(list -> list.toString());
}
Creating streams is also easy, you just need to return Publisher
object.
@GET
@Path("/stream")
@Produces(MediaType.SERVER_SENT_EVENTS)
public Publisher<String> publishers() {
return Flowable
.interval(500, TimeUnit.MILLISECONDS)
.map(s -> atomicInteger.getAndIncrement())
.map(i -> Integer.toString(i));
}
Mutiny and JAX-RS
Apart from the CompletionStage
support, there is also support for Mutiny.
./mvnw quarkus:add-extension
-Dextensions="quarkus-resteasy-mutiny"
@GET
@Produces(MediaType.TEXT_PLAIN)
public Uni<String> hello() {
return Uni.createFrom().item(() -> "hello");
}
@GET
@Produces(MediaType.TEXT_PLAIN)
public Multi<String> multi() {
return Multi.createFrom().items("hello", "world");
}
Mutiny
Quarkus integrates with Mutiny as reactive programming library:
./mvnw quarkus:add-extension
-Dextensions="mutiny"
@ApplicationScoped
public static class ReactiveHello {
public Uni<String> greeting() {
return Uni.createFrom().item(() -> "hello")
.emitOn(Infrastructure.getDefaultExecutor());
}
public Multi<String> stream() {
return Multi.createFrom().items("hello", "world")
.emitOn(Infrastructure.getDefaultExecutor());
}
}
Converting from/to RxJava2
or Reactor
APIs:
RxJava 2
<dependency>
<groupId>io.smallrye.reactive</groupId>
<artifactId>mutiny-rxjava</artifactId>
</dependency>
From RxJava2
:
Uni<Void> uniFromCompletable = Uni.createFrom()
.converter(UniRxConverters.fromCompletable(), completable);
Uni<String> uniFromSingle = Uni.createFrom()
.converter(UniRxConverters.fromSingle(), single);
Uni<String> uniFromObservable = Uni.createFrom()
.converter(UniRxConverters.fromObservable(), observable);
Uni<String> uniFromFlowable = Uni.createFrom()
.converter(UniRxConverters.fromFlowable(), flowable);
...
Multi<Void> multiFromCompletable = Multi.createFrom()
.converter(MultiRxConverters.fromCompletable(), completable);
Multi<String> multiFromObservable = Multi.createFrom()
.converter(MultiRxConverters.fromObservable(), observable);
Multi<String> multiFromFlowable = Multi.createFrom()
.converter(MultiRxConverters.fromFlowable(), flowable);
...
To RxJava2
:
Completable completable = uni.convert().with(UniRxConverters.toCompletable());
Single<Optional<String>> single = uni.convert().with(UniRxConverters.toSingle());
Observable<String> observable = uni.convert().with(UniRxConverters.toObservable());
Flowable<String> flowable = uni.convert().with(UniRxConverters.toFlowable());
...
Completable completable = multi.convert().with(MultiRxConverters.toCompletable());
Single<Optional<String>> single = multi.convert().with(MultiRxConverters.toSingle());
Observable<String> observable = multi.convert().with(MultiRxConverters.toObservable());
Flowable<String> flowable = multi.convert().with(MultiRxConverters.toFlowable());
...
Reactor API
<dependency>
<groupId>io.smallrye.reactive</groupId>
<artifactId>mutiny-reactor</artifactId>
</dependency>
From Reactor
:
Uni<String> uniFromMono = Uni.createFrom().converter(UniReactorConverters.fromMono(), mono);
Uni<String> uniFromFlux = Uni.createFrom().converter(UniReactorConverters.fromFlux(), flux);
Multi<String> multiFromMono = Multi.createFrom().converter(MultiReactorConverters.fromMono(), mono);
Multi<String> multiFromFlux = Multi.createFrom().converter(MultiReactorConverters.fromFlux(), flux);
To Reactor
:
Mono<String> mono = uni.convert().with(UniReactorConverters.toMono());
Flux<String> flux = uni.convert().with(UniReactorConverters.toFlux());
Mono<String> mono2 = multi.convert().with(MultiReactorConverters.toMono());
Flux<String> flux2 = multi.convert().with(MultiReactorConverters.toFlux());
CompletionStages or Publisher
CompletableFuture<String> future = Uni
.createFrom().completionStage(CompletableFuture.supplyAsync(() -> "hello"));
CompletationStage<String> cs = Uni
.createFrom().subscribeAsCompletionStage();
Multi
implements Publisher
.
RESTEasy Reactive
RESTEasy Reactive is a new implementation of JAX-RS but fully reactive.
mvn quarkus:add-extension
-Dextensions="quarkus-resteasy-reactive"
package org.acme.rest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
@Path("rest")
public class Endpoint {
@Path("hello")
@GET
public String hello(){
// executed in event-loop
return "Hello, World!";
}
@GET
public Uni<Book> culinaryGuide(){
// executed in event-loop but not blocking
return Book.findByIsbn("978-2081229297");
}
@io.smallrye.common.annotation.Blocking
@GET
public String blockingHello() throws InterruptedException {
// executed in worker-thread
return "Yaaaawwwwnnnnnn…";
}
}
Reactive Messaging
Quarkus relies on MicroProfile Reactive Messaging spec to implement reactive messaging streams.
mvn quarkus:add-extension
-Dextensions="
io.quarkus:quarkus-smallrye-reactive-messaging"
You can just start using in-memory streams by using @Incoming
to produce data and @Outgoing
to consume data.
Produce every 5 seconds one piece of data.
@ApplicationScoped
public class ProducerData {
@Outgoing("my-in-memory")
public Flowable<Integer> generate() {
return Flowable.interval(5, TimeUnit.SECONDS)
.map(tick -> random.nextInt(100));
}
}
or in Mutiny:
@ApplicationScoped
public class ProducerData {
@Outgoing("my-in-memory")
public Multi<Integer> generate() {
return Multi.createFrom().ticks().every(Duration.ofSeconds(5))
.onItem().apply(n -> random.nextInt(100));
}
}
If you want to dispatch to all subscribers you can annotate the method with @Broadcast
.
Consumes generated data from my-in-memory
stream.
@ApplicationScoped
public class ConsumerData {
@Incoming("my-in-memory")
public void randomNumber(int randomNumber) {
System.out.println("Received " + randomNumber);
}
}
You can also inject an stream as a field:
@Inject
@Stream("my-in-memory") Publisher<Integer> randomRumbers;
@Inject @Stream("generated-price")
Emitter<String> emitter;
Patterns
REST API → Message
@Inject @Stream(“in”)
Emitter<String> emitter;
emitter.send(message);
Message → Message
@Incoming(“in”)
@Outgoing(“out”)
public String process(String in) {
}
Message → SSE
@Inject @Stream(“out”)
Publisher<String> result;
@GET
@Produces(SERVER_SENT_EVENTS)
public Publisher<String> stream() {
return result;
}
Message → Business Logic
@ApplicationScoped
public class ReceiverMessages {
@Incoming("prices")
public void print(String price) {
}
}
To indicate that the method should be executed on a worker pool you can use @Blocking
:
@Outgoing("Y")
@Incoming("X")
@Blocking
To customize:
@Blocking(value="my-custom-pool", ordered = false)
smallrye.messaging.worker.my-custom-pool.max-concurrency=3
Possible implementations are:
In-Memory
If the stream is not configured then it is assumed to be an in-memory stream, if not then stream type is defined by connector
field.
Kafka
To integrate with Kafka you need to add next extensions:
mvn quarkus:add-extension
-Dextensions="
io.quarkus:quarkus-smallrye-reactive-messaging-kafka"
Then @Outgoing
, @Incoming
or @Stream
can be used.
Kafka configuration schema: mp.messaging.[outgoing|incoming].{stream-name}.<property>=<value>
.
The connector
type is smallrye-kafka
.
mp.messaging.outgoing.generated-price.connector=
smallrye-kafka
mp.messaging.outgoing.generated-price.topic=
prices
mp.messaging.outgoing.generated-price.bootstrap.servers=
localhost:9092
mp.messaging.outgoing.generated-price.value.serializer=
org.apache.kafka.common.serialization.IntegerSerializer
mp.messaging.incoming.prices.connector=
smallrye-kafka
mp.messaging.incoming.prices.value.deserializer=
org.apache.kafka.common.serialization.IntegerDeserializer
JSON-B Serializer/Deserializer
You can use JSON-B to serialize/deserialize objects.
./mvnw quarkus:add-extension
-Dextensions="quarkus-kafka-client"
To serialize you can use io.quarkus.kafka.client.serialization.JsonbSerializer
.
To deserialize you need to extend io.quarkus.kafka.client.serialization.JsonbDeserializer
and provide a type.
public class BeerDeserializer
extends JsonbDeserializer<Beer> {
public BeerDeserializer() {
super(Beer.class);
}
}
DevServices
When testing or running in dev mode Quarkus can even provide you with a zero config Kafka out of the box.
Possible configuration values prefixed with quarkus.kafka
:
devservices.enabled
-
If devservices is enabled or not. (default:
true
) devservices.image-name
-
The container image name to use instead of the default one. (default:
docker.io/vectorized/redpanda:v21.5.5
) devservices.port
-
Optional fixed port the dev service will listen to. If not defined, the port will be chosen randomly.
devservices.shared
-
Indicates if the Kafka broker managed by Quarkus Dev Services is shared. When shared, Quarkus looks for running containers using label-based service discovery. (default:
true
) devservices.service-name
-
The value of the
quarkus-dev-service-kafka
label attached to the started container. (default:kafka
) devservices.topic-partitions-timeout
-
Timeout for admin client calls used in topic creation. (default:
2S
) devservices.topic-partitions
-
The topic-partition pairs to create in the Dev Services Kafka broker. Example:
quarkus.kafka.devservices.topic-partitions.test=2
will create a topic named test with 2 partitions
Apicurio Registry Avro
You can use Apicurio Registry Avro.
./mvnw quarkus:add-extension
-Dextensions="apicurio-registry-avro"
Place schemas at src/main/avro/
directory.
mp.messaging.outgoing.movies.connector=smallrye-kafka
mp.messaging.outgoing.movies.topic=movies
# automatically register the schema with the registry, if not present
mp.messaging.outgoing.movies.apicurio.registry.auto-register=true
# URL of registry
mp.messaging.connector.smallrye-kafka.schema.registry.url=http://localhost:8081
DevServices
When testing or running in dev mode Quarkus can even provide you with a zero config Apicurio Registry out of the box.
Possible configuration values prefixed with quarkus.apicurio-registry
:
devservices.enabled
-
If devservices is enabled or not. (default:
true
) devservices.image-name
-
The container image name to use instead of the default one. (default:
docker.io/apicurio/apicurio-registry-mem:2.1.3.Final
) devservices.port
-
Optional fixed port the dev service will listen to. If not defined, the port will be chosen randomly.
devservices.shared
-
Indicates if the Apicurio managed by Quarkus Dev Services is shared. When shared, Quarkus looks for running containers using label-based service discovery. (default:
true
) devservices.service-name
-
The value of the
quarkus-dev-service-apicurio-registry
label attached to the started container. (default:apicurio-registry
)
AMQP
To integrate with AMQP you need to add next extensions:
./mvnw quarkus:add-extension
-Dextensions="reactive-messaging-amqp"
Then @Outgoing
, @Incoming
or @Stream
can be used.
AMQP configuration schema: mp.messaging.[outgoing|incoming].{stream-name}.<property>=<value>
.
Special properties amqp-username
and amqp-password
are used to configure AMQP broker credentials.
The connector type is smallrye-amqp
.
amqp-username=quarkus
amqp-password=quarkus
# write
mp.messaging.outgoing.generated-price.connector=
smallrye-amqp
mp.messaging.outgoing.generated-price.address=
prices
mp.messaging.outgoing.generated-price.durable=
true
# read
mp.messaging.incoming.prices.connector=
smallrye-amqp
mp.messaging.incoming.prices.durable=
true
A complete list of supported properties for AMQP.
DevServices
When testing or running in dev mode Quarkus can even provide you with a zero config AMQP out of the box.
Possible configuration values prefixed with quarkus.amqp
:
devservices.enabled
-
If devservices is enabled or not. (default:
true
) devservices.image-name
-
The container image name to use instead of the default one. (default:
quay.io/artemiscloud/activemq-artemis-broker:0.1.2
) devservices.port
-
Optional fixed port the dev service will listen to. If not defined, the port will be chosen randomly.
devservices.extra-args
-
The value of the
AMQ_EXTRA_ARGS
environment variable to pass to the container. (default:--no-autotune --mapped --no-fsync
) devservices.shared
-
Indicates if the AMQP broker managed by Quarkus Dev Services is shared. When shared, Quarkus looks for running containers using label-based service discovery. (default:
true
) devservices.service-name
-
The value of the
quarkus-dev-service-aqmp
label attached to the started container. (default:amqp
)
RabbitMQ
To integrate with RabbitMQ you need to add next extensions:
./mvnw quarkus:add-extension
-Dextensions="smallrye-reactive-messaging-rabbitmq"
Then @Outgoing
, @Incoming
or @Stream
can be used.
The connector type is smallrye-rabbitmq
.
RabbitMQ configuration schema: mp.messaging.[outgoing|incoming].{stream-name}.<property>=<value>
.
mp.messaging.incoming.requests.connector=smallrye-rabbitmq
mp.messaging.incoming.requests.queue.name=quote-requests
mp.messaging.incoming.requests.exchange.name=quote-requests
mp.messaging.outgoing.quotes.connector=smallrye-rabbitmq
mp.messaging.outgoing.quotes.exchange.name=quotes
A complete list of supported properties for RabbitMQ.
DevServices
When testing or running in dev mode Quarkus can even provide you with a zero config RabbitMQ out of the box.
Possible configuration values prefixed with quarkus.rabbitmq
:
devservices.enabled
-
If devservices is enabled or not. (default:
true
) devservices.image-name
-
The container image name to use instead of the default one. (default:
quay.io/kiegroup/kogito-data-index-ephemeral
) devservices.port
-
Optional fixed port the dev service will listen to. If not defined, the port will be chosen randomly.
devservices.shared
-
Indicates if the RabbitMQ managed by Quarkus Dev Services is shared. When shared, Quarkus looks for running containers using label-based service discovery. (default:
true
) devservices.service-name
-
The value of the
quarkus-dev-service-rabbitmq
label attached to the started container. (default:rabbitmq
) devservices.exchanges.<exchanges>.type
-
Type of exchange:
direct
,topic
,headers
,fanout
, etc. (default:direct
) devservices.exchanges.<exchanges>.auto-delete
-
Should the exchange be deleted when all queues are finished using it? (default:
false
) devservices.exchanges.<exchanges>.durable
-
Should the exchange remain after restarts?
devservices.exchanges.<exchanges>.arguments
-
Extra arguments for the exchange definition.
devservices.queues.<queues>.auto-delete
-
Should the queue be deleted when all consumers are finished using it? (default:
false
) devservices.queues.<queues>.durable
-
Should the queue remain after restarts? (default:
false
) devservices.queues.<queues>.arguments
-
Extra arguments for the queue definition
devservices.bindings.<bindings>.source
-
Source exchange to bind to. Defaults to name of binding instance
devservices.bindings.<bindings>.routing-key
-
Routing key specification for the source exchange (default:
#
) devservices.bindings.<bindings>.destination
-
Destination exchange or queue to bind to. Defaults to name of binding instance
devservices.bindings.<bindings>.destination-type
-
Destination type for binding: queue, exchange, etc. (default:
queue
) devservices.bindings.<bindings>.arguments
-
Extra arguments for the binding definition
MQTT
To integrate with MQTT you need to add next extensions:
./mvnw quarkus:add-extension
-Dextensions="vertx, smallrye-reactive-streams-operators
smallrye-reactive-messaging"
And add io.smallrye.reactive:smallrye-reactive-messaging-mqtt-1.0:0.0.10
dependency in your build tool.
Then @Outgoing
, @Incoming
or @Stream
can be used.
MQTT configuration schema: mp.messaging.[outgoing|incoming].{stream-name}.<property>=<value>
.
The connector type is smallrye-mqtt
.
mp.messaging.outgoing.topic-price.type=
smallrye-mqtt
mp.messaging.outgoing.topic-price.topic=
prices
mp.messaging.outgoing.topic-price.host=
localhost
mp.messaging.outgoing.topic-price.port=
1883
mp.messaging.outgoing.topic-price.auto-generated-client-id=
true
mp.messaging.incoming.prices.type=
smallrye-mqtt
mp.messaging.incoming.prices.topic=
prices
mp.messaging.incoming.prices.host=
localhost
mp.messaging.incoming.prices.port=
1883
mp.messaging.incoming.prices.auto-generated-client-id=
true
Kafka Streams
Create streaming queries with the Kafka Streams API.
./mvnw quarkus:add-extension
-Dextensions="kafka-streams"
All we need to do for that is to declare a CDI producer method which returns the Kafka Streams org.apache.kafka.streams.Topology
:
@ApplicationScoped
public class TopologyProducer {
@Produces
public Topology buildTopology() {
org.apache.kafka.streams.StreamsBuilder.StreamsBuilder
builder = new StreamsBuilder();
// ...
builder.stream()
.join()
// ...
.toStream()
.to();
return builder.build();
}
}
Previous example produces content to another stream. If you want to write interactive queries, you can use Kafka streams.
@Inject
KafkaStreams streams;
return streams
.store("stream", QueryableStoreTypes.keyValueStore());
The Kafka Streams extension is configured via the Quarkus configuration file application.properties
.
quarkus.kafka-streams.bootstrap-servers=localhost:9092
quarkus.kafka-streams.application-id=temperature-aggregator
quarkus.kafka-streams.application-server=${hostname}:8080
quarkus.kafka-streams.topics=weather-stations,temperature-values
kafka-streams.cache.max.bytes.buffering=10240
kafka-streams.commit.interval.ms=1000
IMPORTANT: All the properties within the kafka-streams
namespace are passed through as-is to the Kafka Streams engine. Changing their values requires a rebuild of the application.
Reactive DataSource Properties
Common Reeactive DataSource Client configuration properties prefixed with quarkus.datasource
:
reactive.cache-prepared-statements
-
Prepared statements should be cached on the client side. (default:
false
) reactive.url
-
The datasource URL.
reactive.max-size
-
The datasource pool maximum size.
reactive.trust-all
-
All server certificates should be trusted. (default:
false
) reactive.trust-certificate-pem
-
Trust configuration in the PEM format.
reactive.trust-certificate-jks
-
Trust configuration in the JKS format.
reactive.trust-certificate-pfx
-
Trust configuration in the PFX format.
reactive.key-certificate-pem
-
Key/cert configuration in the PEM format.
reactive.key-certificate-jks
-
Key/cert configuration in the JKS format.
reactive.key-certificate-pfx
-
Key/cert configuration in the PFX format.
reactive.thread-local
-
Use one connection pool per thread.
reactive.reconnect-attempts
-
The number of reconnection attempts when a pooled connection cannot be established on first try. (default:
0
) reactive.reconnect-interval
-
The interval between reconnection attempts when a pooled connection cannot be established on first try. (default:
PT1S
) reactive.idle-timeout
-
The maximum time without data written to or read from a connection before it is removed from the pool.
Reactive PostgreSQL Client
You can use Reactive PostgreSQL to execute queries to PostreSQL database in a reactive way, instead of using JDBC way.
./mvnw quarkus:add-extension
-Dextensions="quarkus-reactive-pg-client"
Database configuration is the same as shown in Persistence section, but URL is different as it is not a jdbc.
quarkus.datasource.db-kind=postgresql
quarkus.datasource.reactive.url=postgresql:///your_database
Then you can inject io.vertx.mutiny.pgclient.PgPool
class.
@Inject
PgPool client;
Uni<List<Fruit>> fruits =
client.preparedQuery("SELECT * FROM fruits")
.onItem().apply(rowSet -> {
JsonArray jsonArray = new JsonArray();
for (Row row : rowSet) {
jsonArray.add(from(row));
}
return jsonArray;
})
Reactive MySQL Client
You can use Reactive MySQL to execute queries to MySQL database in a reactive way, instead of using JDBC.
./mvnw quarkus:add-extension
-Dextensions="quarkus-reactive-mysql-client"
Database configuration is the same as shown in Persistence section, but URL is different as it is not a jdbc.
quarkus.datasource.db-kind=mysql
quarkus.datasource.reactive.url=mysql:///your_database
Then you can inject io.vertx.mutiny.mysqlclient.MySQLPool
class.
Reactive DB2 Client
You can use Reactive DB2 to execute queries to DB2 database in a reactive way, instead of using JDBC.
./mvnw quarkus:add-extension
-Dextensions="quarkus-reactive-db2-client"
Database configuration is the same as shown in Persistence section, but URL is different as it is not a jdbc.
quarkus.datasource.db-kind=db2
quarkus.datasource.reactive.url=vertx-reactive:db2://localhost:50005/hreact
Then you can inject io.vertx.mutiny.db2client.DB2Pool
class.
Reactive Transactions
io.vertx.mutiny.sqlclient.SqlClientHelper
is an util class that allows you to run reactive persisten code within a transaction.
Uni<Void> r = SqlClientHelper.inTransactionUni(client, tx -> {
Uni<RowSet<Row>> insertOne = tx.preparedQuery("INSERT INTO fruits (name) VALUES ($1) RETURNING (id)")
.execute(Tuple.of(fruit1.name));
});
ActiveMQ Artemis
Quarkus uses Reactive Messaging to integrate with messaging systems, but in case you need deeper control when using Apache ActiveMQ Artemis there is also an extension:
./mvnw quarkus:add-extension
-Dextensions="quarkus-artemis-core"
And then you can inject org.apache.activemq.artemis.api.core.client.ServerLocator
instance.
@ApplicationScoped
public class ArtemisConsumerManager {
@Inject
ServerLocator serverLocator;
private ClientSessionFactory connection;
@PostConstruct
public void init() throws Exception {
connection = serverLocator.createSessionFactory();
}
}
And configure ServerLocator
in application.properties
:
quarkus.artemis.url=tcp://localhost:61616
You can configure ActiveMQ Artemis in application.properties
file by using next properties prefixed with quarkus
:
artemis.url
-
Connection URL.
artemis.username
-
Username for authentication.
artemis.password
-
Password for authentication.
Artemis JMS
If you want to use JMS with Artemis, you can do it by using its extension:
./mvnw quarkus:add-extension
-Dextensions="quarkus-artemis-jms"
And then you can inject javax.jms.ConnectionFactory
:
@ApplicationScoped
public class ArtemisConsumerManager {
@Inject
ConnectionFactory connectionFactory;
private Connection connection;
@PostConstruct
public void init() throws JMSException {
connection = connectionFactory.createConnection();
connection.start();
}
}
Configuration options are the same as Artemis core. |
Vert.X Reactive Clients
Vert.X Reactive clients in Quarkus, the next clients are supported and you need to add the dependency to use them:
- Vert.X Mail Client
-
io.smallrye.reactive:smallrye-mutiny-vertx-mail-client
- Vert.X MongoDB Client
-
io.smallrye.reactive:smallrye-mutiny-vertx-mongo-client
- Vert.X Redis Client
-
io.smallrye.reactive:smallrye-mutiny-vertx-redis-client
- Vert.X Cassandra Client
-
io.smallrye.reactive:smallrye-mutiny-vertx-cassandra-client
- Vert.X Consul Client
-
io.smallrye.reactive:smallrye-mutiny-vertx-consul-client
- Vert.X Kafka Client
-
io.smallrye.reactive:smallrye-mutiny-vertx-kafka-client
- Vert.X AMQP Client
-
io.smallrye.reactive:smallrye-mutiny-vertx-amqp-client
- Vert.X RabbitMQ Client
-
io.smallrye.reactive:smallrye-mutiny-vertx-rabbitmq-client
Example of Vert.X Web Client:
@Inject
Vertx vertx;
private WebClient client;
@PostConstruct
void initialize() {
this.client = WebClient.create(vertx, ...);
}
Amazon SQS Client
./mvnw quarkus:add-extension
-Dextensions="amazon-sqs"
Injecting the client:
@Inject
software.amazon.awssdk.services.sqs.SqsClient sqs;
SendMessageResponse response = sqs.sendMessage(m -> m.queueUrl(queueUrl).messageBody(message));
List<Message> messages = sqs.receiveMessage(m -> m.maxNumberOfMessages(10).queueUrl(queueUrl)).messages();
And configure it:
quarkus.sqs.endpoint-override=http://localhost:8010
quarkus.sqs.aws.region=us-east-1
quarkus.sqs.aws.credentials.type=static
quarkus.sqs.aws.credentials.static-provider.access-key-id=test-key
quarkus.sqs.aws.credentials.static-provider.secret-access-key=test-secret
You need to set a HTTP client either URL Connection
:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>url-connection-client</artifactId>
</dependency>
or Apache HTTP:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>apache-client</artifactId>
</dependency>
quarkus.sqs.sync-client.type=apache
You can go async by using Mutiny:
@Inject
software.amazon.awssdk.services.sqs.SqsAsyncClient sqs;
Uni.createFrom()
.completionStage(
sqs.sendMessage(m -> m.queueUrl(queueUrl).messageBody(message))
)
.onItem()...
return Uni.createFrom()
.completionStage(
sqs.receiveMessage(m -> m.maxNumberOfMessages(10).queueUrl(queueUrl))
)
.onItem()
And you need to add the asynchronous Netty client:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>netty-nio-client</artifactId>
</dependency>
Configuration properties are the same as Amazon DynamoDB but changing the prefix from dynamodb
to sqs
.
TLS
You can trust all certificates globally by using quarkus.tls.trust-all=true
RBAC
You can set RBAC using annotations or in application.properties
.
Annotations
You can define roles by using javax.annotation.security.RolesAllowed
annotation.
@RolesAllowed("Subscriber")
You can use io.quarkus.security.Authenticated
as a shortcut of @RolesAllowed("*")
.
To alter RBAC behaviour there are two configuration properties:
quarkus.security.deny-unannotated=true
Configuration options:
quarkus.jaxrs.deny-uncovered
-
If true denies by default to all JAX-RS endpoints. (default:
false
) quarkus.security.deny-unannotated
-
If true denies by default all CDI methods and JAX-RS endpoints. (default:
false
)
By default in Quarkus, if an incoming request has a credential the request will always be authenticated (even if the target page does not require authentication).
You can change this behaviour by setting quarkus.http.auth.proactive
property to false
.
File Configuration
Defining RBAC in application.properties
instead of using annotations.
quarkus.http.auth.policy.role-policy1.roles-allowed=
user,admin
quarkus.http.auth.permission.roles1.paths=
/roles-secured/*,/other/*,/api/*
quarkus.http.auth.permission.roles1.policy=
role-policy1
quarkus.http.auth.permission.permit1.paths=
/public/*
quarkus.http.auth.permission.permit1.policy=
permit
quarkus.http.auth.permission.permit1.methods=
GET
quarkus.http.auth.permission.deny1.paths=
/forbidden
quarkus.http.auth.permission.deny1.policy=
deny
You need to provide permissions set by using the roles-allowed
property or use the built-in ones deny
, permit
or authenticated
.
You can use enabled
property (ie quarkus.http.auth.permission.permit1.enabled
) to enable the entire permission set.
Testing
Quarkus provides explicit support for testing with different users, and with the security subsystem disabled.
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-security</artifactId>
<scope>test</scope>
</dependency>
@Test
@TestSecurity(authorizationEnabled = false)
void someTestMethod() {
...
}
@Test
@TestSecurity(user = "testUser", roles = {"admin", "user"})
void someTestMethod() {
...
}
BouncyCastle
Quarkus supports BouncyCastle, you only need to add the BouncyCastle dependency and configure the security provider:
quarkus.security.security-providers=BC
quarkus.security.security-providers=BCJSSE
quarkus.security.security-providers=BCFIPS
quarkus.security.security-providers=BCFIPSJSSE
JWT
Quarkus implements MicroProfile JWT RBAC spec.
mvn quarkus:add-extension
-Dextensions="io.quarkus:quarkus-smallrye-jwt"
Minimum JWT required claims: typ
, alg
, kid
, iss
, sub
, exp
, iat
, jti
, upn
, groups
.
You can inject token by using JsonWebToken
or a claim individually by using @Claim
.
@Inject
JsonWebToken jwt;
@Inject
@Claim(standard = Claims.preferred_username)
String name;
@Inject
@Claim("groups")
Set<String> groups;
@Inject
JWTParser parser;
Set of supported types: String
, Set<String>
, Long
, Boolean, `javax.json.JsonValue
, Optional
, org.eclipse.microprofile.jwt.ClaimValue
.
And configuration in src/main/resources/application.properties
:
mp.jwt.verify.publickey.location=
META-INF/resources/publicKey.pem
mp.jwt.verify.issuer=
https://quarkus.io/using-jwt-rbac
Configuration options:
mp.jwt.verify.publickey
-
Public Key text itself to be supplied as a string.
mp.jwt.verify.publickey.location
Relative path or URL of a public key.
mp.jwt.verify.issuer
-
iss
accepted as valid.
smallrye.jwt.token.header
-
Sets header such as
Cookie
is used to pass the token. (default:Authorization
). smallrye.jwt.token.cookie
-
Name of the cookie containing a token.
smallrye.jwt.token.schemes
-
Comma-separated list containing an alternative single or multiple schemes. (default:
Bearer
). smallrye.jwt.require.named-principal
-
A token must have a upn or preferred_username or sub claim set if using
java.security.Principal
.True
makes throw an exception if not set. (default:false
). smallrye.jwt.path.sub
-
Path to the claim with subject name.
smallrye.jwt.claims.sub
-
Default sub claim value.
smallrye.jwt.path.groups
-
Path to the claim containing the groups.
smallrye.jwt.groups-separator
-
Separator for splitting a string which may contain multiple group values. (default. ` `).
smallrye.jwt.claims.groups
-
Default groups claim value.
smallrye.jwt.jwks.refresh-interval
-
JWK cache refresh interval in minutes. (default:
60
). smallrye.jwt.expiration.grace
-
Expiration grace in seconds. (default:
60
). smallrye.jwt.verify.aud
-
Comma separated list of the audiences that a token aud claim may contain.
smallrye.jwt.verify.algorithm
-
Signature algorith. (defsult:
RS256
) smallrye.jwt.token.kid
-
If set then the verification JWK key as well every JWT token must have a matching
kid
header. smallrye.jwt.time-to-live
-
The maximum number of seconds that a JWT may be issued for use.
smallrye.jwt.sign.key-location
-
Location of a private key which will be used to sign the claims when either a no-argument
sign()
orinnerSign()
method is called. smallrye.jwt.encrypt.key-location
-
Location of a public key which will be used to encrypt the claims or inner JWT when a no-argument
encrypt()
method is called.
Supported public key formats:
-
PKCS#8 PEM
-
JWK
-
JWKS
-
JWK Base64 URL
-
JWKS Base64 URL
To send a token to server-side you should use Authorization
header: curl -H "Authorization: Bearer eyJraWQiOi…"
.
To inject claim values, the bean must be @RequestScoped
CDI scoped.
If you need to inject claim values in scope with a lifetime greater than @RequestScoped
then you need to use javax.enterprise.inject.Instance
interface.
@Inject
@Claim(standard = Claims.iat)
private Instance<Long> providerIAT;
RBAC
JWT groups
claim is directly mapped to roles to be used in security annotations.
@RolesAllowed("Subscriber")
Testing
You can use @TestSecurity
annotation tooin OIDC by registering the following dependency:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-security-jwt</artifactId>
<scope>test</scope>
</dependency>
@Test
@TestSecurity(user = "userJwt", roles = "viewer")
@JwtSecurity(claims = {
@Claim(key = "email", value = "user@gmail.com")
})
public void testJwtWithClaims() {}
Generate tokens
JWT generation API:
Jwt.claims()
.issuer("https://server.com")
.claim("customClaim", 3)
.sign(createKey());
JwtSignatureBuilder jwtSignatureBuilder = Jwt.claims("/testJsonToken.json").jws();
jwtSignatureBuilder
.signatureKeyId("some-key-id")
.signatureAlgorithm(SignatureAlgorithm.ES256)
.header("custom-header", "custom-value");
.sign(createKey());
Jwt.claims("/testJsonToken.json")
.encrypt(createKey());
JwtEncryptionBuilder jwtEncryptionBuilder = Jwt.claims("/testJsonToken.json").jwe();
jwtEncryptionBuilder
.keyEncryptionKeyId("some-key-id")
.keyEncryptionAlgorithm(KeyEncryptionAlgorithm.ECDH_ES_A256KW)
.header("custom-header", "custom-value");
.encrypt(createKey());
Jwt.claims("/testJsonToken.json")
.innerSign(createKey());
.encrypt(createKey());
OpenId Connect
Quarkus can use OpenId Connect or OAuth 2.0 authorization servers such as Keycloak to protect resources using bearer token issued by Keycloak server.
mvn quarkus:add-extension
-Dextensions="using-openid-connect"
You can also protect resources with security annotations.
@GET
@RolesAllowed("admin")
You can inject the folowing Oidc objects in a class:
@Inject
io.quarkus.oidc.UserInfo userInfo;
@Inject
io.quarkus.oidc.OidcConfigurationMetadata configMetadata;
Configure application to Keycloak service in application.properties
file.
quarkus.oidc.realm=quarkus
quarkus.oidc.auth-server-url=http://localhost:8180/auth
quarkus.oidc.resource=backend-service
quarkus.oidc.bearer-only=true
quarkus.oidc.credentials.secret=secret
quarkus-oidc-client
can be configured as follows to support MTLS
:
quarkus.oidc.tls.verification=certificate-validation
quarkus.oidc.client.tls.key-store-file=client-keystore.jks
quarkus.oidc.client.tls.key-store-password=${key-store-password}
quarkus.oidc.client.tls.key-store-alias=keyAlias
quarkus.oidc.client.tls.key-store-alias-password=keyAliasPassword
quarkus.oidc.client.tls.trust-store-file=client-truststore.jks
quarkus.oidc.client.tls.trust-store-password=${trust-store-password}
quarkus.oidc.client.tls.trust-store-alias=certAlias
There is also support for the public OIDC providers:
GitHub
quarkus.oidc.provider=github
quarkus.oidc.client-id=github_app_clientid
quarkus.oidc.credentials.secret=github_app_clientsecret
Apple
quarkus.oidc.provider=apple
quarkus.oidc.client-id=${apple.client-id}
quarkus.oidc.credentials.jwt.token-key-id=${apple.key-id}
quarkus.oidc.provider=google
quarkus.oidc.client-id={GOOGLE_CLIENT_ID}
quarkus.oidc.credentials.secret={GOOGLE_CLIENT_SECRET}
quarkus.oidc.token.issuer=https://accounts.google.com
Configuration options with quarkus.oidc
prefix:
enabled
-
The OIDC is enabled. (default:
true
) tenant-enabled
-
If the tenant configuration is enabled. (default:
true
) application-type
-
The application type. Possible values:
web_app
,service
. (default:service
) connection-delay
-
The maximum amount of time the adapter will try connecting.
auth-server-url
-
The base URL of the OpenID Connect (OIDC) server.
introspection-path
-
Relative path of the RFC7662 introspection service.
jwks-path
-
Relative path of the OIDC service returning a JWK set.
public-key
-
Public key for the local JWT token verification
client-id
-
The client-id of the application.
roles.role-claim-path
-
Path to the claim containing an array of groups. (
realm/groups
) roles.role-claim-separator
-
Separator for splitting a string which may contain multiple group values.
token.issuer
-
Issuer claim value.
token.audience
-
Audience claim value.
token.expiration-grace
-
Expiration grace period in seconds.
token.principal-claim
-
Name of the claim which contains a principal name.
token.refresh-expired
-
If property is enabled then a refresh token request is performed.
credentials.secret
-
The client secret
authentication.redirect-path
-
Relative path for calculating a
redirect_uri
query parameter. authentication.restore-path-after-redirect
-
The original request URI used before the authentication will be restored after the user has been redirected back to the application. (default:
true
) authentication.force-redirect-https-scheme
-
Force 'https' as the 'redirect_uri' parameter scheme when running behind an SSL terminating reverse proxy.
authentication.scopes
-
List of scopes.
authentication.extra-params
-
Additional properties which will be added as the query parameters .
authentication.cookie-path
-
Cookie path parameter.
proxy.host
-
The host (name or IP address) of the Proxy.
proxy.port
-
The port number of the Proxy. (default:
80
) proxy.username
-
The username to authenticate.
proxy.password
-
The password to authenticate.
end-session-path
-
Relative path of the OIDC
end_session_endpoint
. logout.path
-
The relative path of the logout endpoint at the application.
logout.post-logout-path
-
Relative path of the application endpoint where the user should be redirected to after logging out.
tls.verification
-
Sets the TLs verification. Possible values:
REQUIRED
,NONE
. (default:REQUIRED
).
With Keycloak OIDC server https://host:port/auth/realms/{realm} where {realm} has to be replaced by the name of the Keycloak realm.
|
You can use quarkus.http.cors property to enable consuming form different domain.
|
Testing
You can use @TestSecurity
annotation tooin OIDC by registering the following dependency:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-security-oidc</artifactId>
<scope>test</scope>
</dependency>
@Test
@TestSecurity(user = "userOidc", roles = "viewer")
@OidcSecurity(claims = {
@Claim(key = "email", value = "user@gmail.com")
}, userinfo = {
@UserInfo(key = "sub", value = "subject")
}, config = {
@ConfigMetadata(key = "issuer", value = "issuer")
})
public void testOidcWithClaimsUserInfoAndMetadata() {}
DevServices
When testing or running in dev mode Quarkus can even provide you with a zero config Keycloak out of the box.
Possible configuration values prefixed with quarkus.keycloak
:
devservices.enabled
-
If devservices is enabled or not. (default:
true
) devservices.image-name
-
The container image name to use instead of the default one. Keycloak-X can be used (
quay.io/keycloak/keycloak-x:15.0.2
) (default:quay.io/keycloak/keycloak:15.0.2
) devservices.keycloak-x-image
-
Set
quarkus.devservices.keycloak.keycloak-x-image
to override this check which may be necessary if you build custom Keycloak-X or Keycloak images. You do not need to set this property if the default check works. devservices.port
-
Optional fixed port the dev service will listen to. If not defined, the port will be chosen randomly.
devservices.shared
-
Indicates if the Keycloak broker managed by Quarkus Dev Services is shared. When shared, Quarkus looks for running containers using label-based service discovery. (default:
true
) devservices.service-name
-
The value of the
quarkus-dev-service-keycloak
label attached to the started container. (default:kafka
) devservices.realm-path
-
The class or file system path to a Keycloak realm file which will be used to initialize Keycloak
devservices.java-opts
-
The JAVA_OPTS passed to the keycloak JVM
devservices.realm-name
-
The Keycloak realm name
devservices.create-realm
-
Indicates if the Keycloak realm has to be created when the realm file pointed to by the 'realm-path' property does not exist
devservices.users
-
The Keycloak users map containing the user name and password pairs
devservices.roles
-
The Keycloak user roles
Multi-tenancy
Multi-tenancy is supported by adding a sub-category to OIDC configuration properties (ie quarkus.oidc.{tenent_id}.property
).
quarkus.oidc.auth-server-url=http://localhost:8180/auth/realms/quarkus
quarkus.oidc.client-id=multi-tenant-client
quarkus.oidc.application-type=web-app
quarkus.oidc.tenant-b.auth-server-url=https://accounts.google.com
quarkus.oidc.tenant-b.application-type=web-app
quarkus.oidc.tenant-b.client-id=xxxx
quarkus.oidc.tenant-b.credentials.secret=yyyy
quarkus.oidc.tenant-b.token.issuer=https://accounts.google.com
quarkus.oidc.tenant-b.authentication.scopes=email,profile,openid
You can inject io.quarkus.oidc.client.Tokens
, io.quarkus.oidc.OidcSession
, and io.quarkus.oidc.client.OidcClient
to interact with OIDC.
@Inject
@io.quarkus.oidc.client.NamedOidcClient("client2")
OidcClient client;
@Inject
@io.quarkus.oidc.client.NamedOidcClient("client1")
Tokens tokens1;
@Inject
OidcSession oidcSession;
OAuth2
Quarkus integrates with OAuth2 to be used in case of opaque tokens (none JWT) and its validation against an introspection endpoint.
mvn quarkus:add-extension
-Dextensions="security-oauth2"
And configuration in src/main/resources/application.properties
:
quarkus.oauth2.client-id=client_id
quarkus.oauth2.client-secret=secret
quarkus.oauth2.introspection-url=http://oauth-server/introspect
And you can map roles to be used in security annotations.
@RolesAllowed("Subscriber")
Configuration options:
quarkus.oauth2.enabled
-
Determine if the OAuth2 extension is enabled. (default:
true
) quarkus.oauth2.client-id
-
The OAuth2 client id used to validate the token.
quarkus.oauth2.client-secret
-
The OAuth2 client secret used to validate the token.
quarkus.oauth2.introspection-url
-
URL used to validate the token and gather the authentication claims.
quarkus.oauth2.role-claim
-
The claim that is used in the endpoint response to load the roles ((default:
scope
)
Authenticating via HTTP
HTTP basic auth is enabled by the quarkus.http.auth.basic=true
property.
HTTP form auth is enabled by the quarkus.http.auth.form.enabled=true
property.
Then you need to add elytron-security-properties-file
or elytron-security-jdbc
.
Security with Properties File
You can also protect endpoints and store identities (user, roles) in the file system.
mvn quarkus:add-extension
-Dextensions="elytron-security-properties-file"
You need to configure the extension with users and roles files:
And configuration in src/main/resources/application.properties
:
quarkus.security.users.file.enabled=true
quarkus.security.users.file.users=test-users.properties
quarkus.security.users.file.roles=test-roles.properties
quarkus.security.users.file.auth-mechanism=BASIC
quarkus.security.users.file.realm-name=MyRealm
quarkus.security.users.file.plain-text=true
Then users.properties
and roles.properties
:
scott=jb0ss
jdoe=p4ssw0rd
scott=Admin,admin,Tester,user
jdoe=NoRolesUser
IMPORTANT: If plain-text
is set to false
(or omitted) then passwords must be stored in the form MD5 (username
:`realm`:`password`).
Elytron File Properties configuration properties.
Prefix quarkus.security.users
is skipped.
file.enabled
-
The file realm is enabled. (default:
false
) file.auth-mechanism
-
The authentication mechanism. ( default:
BASIC
) file.realm-name
-
The authentication realm name. (default:
Quarkus
) file.plain-text
-
If passwords are in plain or in MD5. (default:
false
) file.users
-
Classpath resource of user/password. (default:
users.properties
) file.roles
-
Classpath resource of user/role. (default:
roles.properties
)
Embedded Realm
You can embed user/password/role in the same application.properties
:
quarkus.security.users.embedded.enabled=true
quarkus.security.users.embedded.plain-text=true
quarkus.security.users.embedded.users.scott=jb0ss
quarkus.security.users.embedded.roles.scott=admin,tester,user
quarkus.security.users.embedded.auth-mechanism=BASIC
IMPORTANT: If plain-text is set to false
(or omitted) then passwords must be stored in the form MD5 (username
:`realm`:`password`).
Prefix quarkus.security.users.embedded
is skipped.
algorithm
-
Determine which algorithm to use. Possible values:
DIGEST_MD5
,DIGEST_SHA
,DIGEST_SHA_256
,DIGEST_SHA_384
,DIGEST_SHA_512
,DIGEST_SHA_512_256
. (default:DIGEST_MD5
) file.enabled
-
The file realm is enabled. (default:
false
) file.auth-mechanism
-
The authentication mechanism. (default:
BASIC
) file.realm-name
-
The authentication realm name. (default:
Quarkus
) file.plain-text
-
If passwords are in plain or in MD5. (default:
false
) file.users.*
-
*
is user and value is password. file.roles.*
-
*
is user and value is role.
Security with a JDBC Realm
You can also protect endpoints and store identities in a database.
mvn quarkus:add-extension
-Dextensions="elytron-security-jdbc"
You still need to add the database driver (ie jdbc-h2
).
You need to configure JDBC and Elytron JDBC Realm:
quarkus.datasource.url=
quarkus.datasource.driver=org.h2.Driver
quarkus.datasource.username=sa
quarkus.datasource.password=sa
quarkus.security.jdbc.enabled=true
quarkus.security.jdbc.principal-query.sql=
SELECT u.password, u.role FROM test_user u WHERE u.user=?
quarkus.security.jdbc.principal-query
.clear-password-mapper.enabled=true
quarkus.security.jdbc.principal-query
.clear-password-mapper.password-index=1
quarkus.security.jdbc.principal-query
.attribute-mappings.0.index=2
quarkus.security.jdbc.principal-query
.attribute-mappings.0.to=groups
You need to set the index (1-based) of password and role.
Elytron JDBC Realm configuration properties.
Prefix quarkus.security.jdbc
is skipped.
auth-mechanism
-
The authentication mechanism. (default:
BASIC
) realm-name
-
The authentication realm name. (default:
Quarkus
) enabled
-
If the properties store is enabled. (default:
false
) principal-query.sql
-
The sql query to find the password.
principal-query.datasource
-
The data source to use.
principal-query.clear-password-mapper.enabled
-
If the clear-password-mapper is enabled. (default:
false
) principal-query.clear-password-mapper.password-index
-
The index of column containing clear password. (default:
1
) principal-query.bcrypt-password-mapper.enabled
-
If the bcrypt-password-mapper is enabled. (default:
false
) principal-query.bcrypt-password-mapper.password-index
-
The index of column containing password hash. (default:
0
) principal-query.bcrypt-password-mapper.hash-encoding
-
A string referencing the password hash encoding (
BASE64
orHEX
). (default:BASE64
) principal-query.bcrypt-password-mapper.salt-index
-
The index column containing the Bcrypt salt. (default:
0
) principal-query.bcrypt-password-mapper.salt-encoding
-
A string referencing the salt encoding (
BASE64
orHEX
). (default:BASE64
) principal-query.bcrypt-password-mapper.iteration-count-index
-
The index column containing the Bcrypt iteration count. (default:
0
)
For multiple datasources you can use the datasource name in the properties:
quarkus.datasource.url=
quarkus.security.jdbc.principal-query.sql=
quarkus.datasource.permissions.url=
quarkus.security.jdbc.principal-query.permissions.sql=
Security with JPA
You can also protect endpoints and store identities in a database using JPA.
mvn quarkus:add-extension
-Dextensions="security-jpa"
Also you might require jdbc-postgresql , resteasy , hibernate-orm-panache .
|
@io.quarkus.security.jpa.UserDefinition
@Table(name = "test_user")
@Entity
public class User extends PanacheEntity {
@io.quarkus.security.Username
public String name;
@io.quarkus.security.Password
public String pass;
@ManyToMany
@Roles
public List<Role> roles = new ArrayList<>();
public static void add(String username, String password) {
User user = new User();
user.username = username;
user.password = BcryptUtil.bcryptHash(password);
user.persist();
}
}
@Entity
public class Role extends PanacheEntity {
@ManyToMany(mappedBy = "roles")
public List<ExternalRolesUserEntity> users;
@io.quarkus.security.RolesValue
public String role;
}
You need to configure JDBC:
quarkus.datasource.url=jdbc:postgresql:security_jpa
quarkus.datasource.driver=org.postgresql.Driver
quarkus.datasource.username=quarkus
quarkus.datasource.password=quarkus
quarkus.hibernate-orm.database.generation=drop-and-create
Security with LDAP
You can also protect endpoints and store identities in a database using LDAP.
mvn quarkus:add-extension
-Dextensions="elytron-security-ldap"
quarkus.security.ldap.enabled=true
quarkus.security.ldap.dir-context.principal=uid=tool,ou=accounts,o=YourCompany,c=DE
quarkus.security.ldap.dir-context.url=ldaps://ldap.server.local
quarkus.security.ldap.dir-context.password=PASSWORD
quarkus.security.ldap.identity-mapping.rdn-identifier=uid
quarkus.security.ldap.identity-mapping.search-base-dn=ou=users,ou=tool,o=YourCompany,c=DE
quarkus.security.ldap.identity-mapping.attribute-mappings."0".from=cn
quarkus.security.ldap.identity-mapping.attribute-mappings."0".to=groups
quarkus.security.ldap.identity-mapping.attribute-mappings."0".filter=(member=uid={0})
quarkus.security.ldap.identity-mapping.attribute-mappings."0".filter-base-dn=ou=roles,ou=tool,o=YourCompany,c=DE
Testing
There is a Quarkus Test Resource that starts and stops InMemory LDAP server before and after test suite.
It is running in localhost
with dc=quarkus,dc=io
and binding credentials ("uid=admin,ou=system", "secret"
).
Imports LDIF from a file located at root of the classpath named quarkus-io.ldif
.
Register dependency io.quarkus:quarkus-test-ldap:test.
And annotate the test:
@QuarkusTestResource(io.quarkus.test.ldap.LdapServerTestResource.class)
public class ElytronLdapExtensionTestResources {
}
Elytron LDAP Realm configuration properties.
Prefix quarkus.security.ldap
is skipped.
enabled
-
Enable the LDAP elytron module (default:
false
) realm-name
-
The elytron realm name (default:
Quarkus
) direct-verification
-
Provided credentials are verified against LDAP (default:
true
) dir-context.url
-
The url of the LDAP server.
dir-context.principal
-
User (
bindDn
) which is used to connect to LDAP server. dir-context.password
-
The password (
bindCredential
) which belongs to the principal. identity-mapping.rdn-identifier
-
The identifier (
baseFilter
) which correlates to the provided user (default:uid
) identity-mapping.search-base-dn
-
The dn where we look for users.
identity-mapping.attribute-mappings.<id>.from
-
The
roleAttributeId
from which is mapped identity-mapping.attribute-mappings.<id>.to
-
The identifier whom the attribute is mapped to (default:
gropus
) identity-mapping.attribute-mappings.<id>.filter
-
The filter (
roleFilter
) identity-mapping.attribute-mappings.<id>.filter-base-dn
-
The filter base dn (
rolesContextDn
)
Vault
Quarkus integrates with Vault to manage secrets or protecting sensitive data.
mvn quarkus:add-extension
-Dextensions="vault"
And configuring Vault in application.properties
:
# vault url
quarkus.vault.url=http://localhost:8200
quarkus.vault.authentication.userpass.username=
bob
quarkus.vault.authentication.userpass.password=
sinclair
# path within the kv secret engine
quarkus.vault.secret-config-kv-path=
myapps/vault-quickstart/config
quarkus.vault.secret-config-kv-prefix.singer.paths=
multi/singer1, multi/singer2
vault kv put secret/myapps/vault-quickstart/config a-private-key=123456
vault kv put secret/multi/singer1 firstname=paul
@ConfigProperty(name = "a-private-key")
String privateKey;
@ConfigProperty(name = "singer.firstname")
String firstName;
You can access the KV engine programmatically:
@Inject
VaultKVSecretEngine kvSecretEngine;
kvSecretEngine.readSecret("myapps/vault-quickstart/" + vaultPath).toString();
Map<String, String> secrets;
kvSecretEngine.writeSecret("myapps/vault-quickstart/crud", secrets);
kvSecretEngine.deleteSecret("myapps/vault-quickstart/crud");
Fetching credentials DB
With the next kv vault kv put secret/myapps/vault-quickstart/db password=connor
quarkus.vault.credentials-provider.mydatabase.kv-path=
myapps/vault-quickstart/db
quarkus.datasource.db-kind=
postgresql
quarkus.datasource.username=
sarah
quarkus.datasource.credentials-provider=
mydatabase
quarkus.datasource.jdbc.url=
jdbc:postgresql://localhost:5432/mydatabase
No password is set as it is fetched from Vault.
Dynamic credentials are also supported:
Running the following dynamic database config in Vault:
vault write database/config/mydb plugin_name=postgresql-database-plugin …..
You can configure as:
quarkus.vault.credentials-provider
.mydatabase.database-credentials-role=mydbrole
quarkus.datasource.db-kind=
postgresql
quarkus.datasource.credentials-provider=
mydatabase
quarkus.datasource.jdbc.url=
jdbc:postgresql://localhost:5432/mydatabase
Username and password are fetched from Vault
Transit
@Inject
VaultTransitSecretEngine transit;
transit.encrypt("my_encryption", text);
transit.decrypt("my_encryption", text).asString();
transit.sign("my-sign-key", text);
Transit Key
@Inject
VaultTransitSecretEngine transit;
transit.createKey(KEY_NAME, new KeyCreationRequestDetail().setExportable(true));
transit.readKey(KEY_NAME);
transit.listKeys();
transit.exportKey(KEY_NAME, VaultTransitExportKeyType.encryption, null);
transit.updateKeyConfiguration(KEY_NAME, new KeyConfigRequestDetail().setDeletionAllowed(true));
transit.deleteKey(KEY_NAME);
Vault TOTP
TOTP secret engine is supported by using io.quarkus.vault.VaultTOTPSecretEngine
class:
@Inject
VaultTOTPSecretEngine vaultTOTPSecretEngine;
CreateKeyParameters createKeyParameters = new CreateKeyParameters("Google", "test@gmail.com");
createKeyParameters.setPeriod("30m");
/** Generate Key (QR code) */
final Optional<KeyDefinition> myKey = vaultTOTPSecretEngine
.createKey("my_key_2", createKeyParameters);
/** Generate key number to login */
final String keyCode = vaultTOTPSecretEngine.generateCode("my_key_2");
/** Login logic */
boolean valid = vaultTOTPSecretEngine.validateCode("my_key_2", keyCode);
Vault Provisioning
Vault extension offers façade classes to Vault provisioning functions:
@Inject
VaultSystemBackendEngine vaultSystemBackendEngine;
@Inject
VaultKubernetesAuthService vaultKubernetesAuthService;
String rules = "path \"transit/*\" {\n" +
" capabilities = [ \"create\", \"read\", \"update\" ]\n" +
"}";
String policyName = "sys-test-policy";
vaultSystemBackendEngine.createUpdatePolicy(policyName, rules);
vaultKubernetesAuthService
.createRole(roleName, new VaultKubernetesAuthRole()
.setBoundServiceAccountNames(boundServiceAccountNames)
.setBoundServiceAccountNamespaces(boundServiceAccountNamespaces)
.setTokenPolicies(tokenPolicies));
PKI
@Inject
public VaultPKISecretEngine pkiSecretEngine;
GenerateCertificateOptions options = new GenerateCertificateOptions();
SignedCertificate signed = pkiSecretEngine.signRequest("example-dot-com", csr, options);
return signed.certificate.getData();
Vault configuration properties.
Prefix quarkus.vault
is skipped.
url
-
Vault server URL
authentication.client-token
-
Vault token to access
authentication.app-role.role-id
-
Role Id for AppRole auth
authentication.app-role.secret-id
-
Secret Id for AppRole auth
authentication.app-role.secret-id-wrapping-token
-
Wrapping token containing a Secret Id.
secret-id
andsecret-id-wrapping-token
are exclusive. authentication.userpass.username
-
Username for userpass auth
authentication.userpass.password
-
Password for userpass auth
authentication.userpass.password-wrapping-token
-
Wrapping token containing a password.
password
andpassword-wrapping-token
are exclusive. authentication.kubernetes.role
-
Kubernetes authentication role
authentication.kubernetes.jwt-token-path
-
Location of the file containing the Kubernetes JWT token
renew-grace-period
-
Renew grace period duration (default:
1H
) secret-config-cache-period
-
Vault config source cache period (default:
10M
) secret-config-kv-path
-
Vault path in kv store. List of paths is supported in CSV
log-confidentiality-level
-
Used to hide confidential infos.
low
,medium
,high
(default:medium
) kv-secret-engine-version
-
Kv secret engine version (default: 1)
kv-secret-engine-mount-path
Kv secret engine path (default: secret
)
tls.skip-verify
-
Allows to bypass certificate validation on TLS communications (default:
false
) tls.ca-cert
-
Certificate bundle used to validate TLS communications
tls.use-kubernetes-ca-cert
-
TLS will be active (default:
true
) connect-timeout
-
Tiemout to establish a connection (default:
5S
) read-timeout
-
Request timeout (default:
1S
) credentials-provider."credentials-provider".database-credentials-role
-
Database credentials role
credentials-provider."credentials-provider".kv-path
-
A path in vault kv store, where we will find the kv-key
credentials-provider."credentials-provider".kv-key
-
Key name to search in vault path kv-path (default:
password
)
DevServices
When testing or running in dev mode Quarkus can even provide you with a zero config Vault out of the box.
Possible configuration values are shown at Vault
Amazon KMS
mvn quarkus:add-extension
-Dextensions="amazon-kms"
@Inject
KmsClient kms;
kms.encrypt(req -> req.keyId(keyArn).plaintext(
SdkBytes.fromUtf8String(data))).ciphertextBlob();
quarkus.kms.endpoint-override=http://localhost:8011
quarkus.kms.aws.region=us-east-1
quarkus.kms.aws.credentials.type=static
quarkus.kms.aws.credentials.static-provider.access-key-id=test-key
quarkus.kms.aws.credentials.static-provider.secret-access-key=test-secret
You need to set a HTTP client either URL Connection
:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>url-connection-client</artifactId>
</dependency>
or Apache HTTP:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>apache-client</artifactId>
</dependency>
quarkus.sqs.sync-client.type=apache
You can go async by using Mutiny:
@Inject
software.amazon.awssdk.services.kms.KmsAsyncClient kms;
Uni.createFrom().completionStage(
kms.encrypt(req -> req.keyId(keyArn).plaintext(SdkBytes.fromUtf8String(data))
))
And you need to add the asynchronous Netty client:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>netty-nio-client</artifactId>
</dependency>
Configuration properties are the same as Amazon DynamoDB but changing the prefix from dynamodb
to kms
.
Amazon IAM
mvn quarkus:add-extension
-Dextensions="quarkus-amazon-iam"
You need to set a HTTP client either URL Connection
:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>url-connection-client</artifactId>
</dependency>
or Apache HTTP:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>apache-client</artifactId>
</dependency>
And you need to add the asynchronous Netty client:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>netty-nio-client</artifactId>
</dependency>
@Inject
IamClient client;
@Inject
IamAsyncClient async;
quarkus.iam.endpoint-override=${iam.url}
quarkus.iam.aws.region=us-east-1
quarkus.iam.aws.credentials.type=static
quarkus.iam.aws.credentials.static-provider.access-key-id=test-key
quarkus.iam.aws.credentials.static-provider.secret-access-key=test-secret
Configuration properties are the same as Amazon DynamoDB but changing the prefix from dynamodb
to iam
.
HTTP Configuration
You can configure HTTP parameters.
Using quarkus.http
prefix:
http.access-log.exclude-pattern
-
A regular expression that can be used to exclude some paths from logging
cors
-
Enable CORS. (default:
false
) cors.origins
-
CSV of origins allowed. Regular expressions are also allowed (dedault: Any request valid.)
cors.methods
-
CSV of methods valid. (default: Any method valid.)
cors.headers
-
CSV of valid allowed headers. (default: Any requested header valid.)
cors.exposed-headers
-
CSV of valid exposed headers.
port
-
The HTTP port. (default:
8080
) test-port
-
The HTTP test port. (default:
8081
) host
-
The HTTP host. (default:
0.0.0.0
) host-enabled
-
Enable listening to host:port. (default:
true
) ssl-port
-
The HTTPS port. (default
8443
) test-ssl-port
-
The HTTPS port used to run tests. (default:
8444
) insecure-requests
-
If insecure requests are allowed. Possible values:
enabled
,redirect
,disable
. (default:enabled
) http2
-
Enables HTTP/2. (default:
true
) proxy.proxy-address-forwarding
-
The address, scheme etc will be set from headers forwarded by the proxy server.
proxy.allow-forwarded
-
Proxy address forwarding is enabled then the standard
Forwarded
header will be used, rather than the more common but not standardX-Forwarded-For
. proxy.enable-forwarded-host
-
Enable override the received request’s host through a forwarded host header
proxy.forwarded-host-header
-
Configure the forwarded host header to be used if override enabled
ssl.port
-
The HTTPS port. (default:
8443
) ssl.certificate.files
-
The files path to a service certificate or certificate chain in PEM format. Relative to
src/main/resources
. ssl.certificate.key-files
-
The files path to the corresponding certificate private key in PEM format. Relative to
src/main/resources
. ssl.certificate.key-store-file
-
The key store contains the certificate information. Relative to
src/main/resources
. ssl.certificate.key-store-key-alias
-
An optional parameter to select a specific key in the key store.
ssl.certificate.key-store-file-type
-
The key store type. It is automatically detected based on the file name or can be set manually. Supported values are:
JKS
,JCEKS
,P12
,PKCS12
orPFX
. ssl.certificate.key-store-password
-
The password to open the key store file.
ssl.certificate.trust-store-file
-
The trust store location which contains the certificate information of the certificates to trust. Relative to
src/main/resources
. ssl.certificate.trust-store-cert-alias
-
An optional parameter to trust only one specific certificate in the trust store
ssl.certificate.trust-store-file-type
-
The trust store type. It is automatically detected based on the file name or can be set manually.
ssl.certificate.trust-store-password
-
The password to open the trust store file.
ssl.cipher-suites
-
A list of strings of cipher suites to use. If not provided, a reasonable default is selected.
ssl.protocols
-
The list of protocols to explicitly enable. (default:
TLSv1.3
andTLSv1.2
). ssl.sni
-
Enables Server Name Indication (SNI), an TLS extension allowing the server to use multiple certificates. (default:
false
) ssl.client-auth
-
Configures the engine to require/request client authentication. Possible values are:
none
,request
andrequired
. (default:none
). io-threads
-
The number if IO threads used to perform IO.
limits.max-header-size
-
The maximum length of all headers. (default:
20k
) limits.max-body-size
-
The maximum size of a request body. (default:
10M
) limits.max-chunk-size
-
The max HTTP chunk size.
limits.max-initial-line-length
-
The maximum length of the initial line. (default:
4096
) idle-timeout
-
Http connection idle timeout. (default:
30M
) enable-compression
-
If responses should be compressed.
read-timeout
-
Http connection read timeout for blocking IO. (default:
60s
) body.handle-file-uploads
-
If the files sent using
multipart/form-data
will be stored locally. (default:true
) body.uploads-directory
-
The directory where the files sent using
multipart/form-data
should be stored. (default:file-uploads
) body.merge-from-attributes
-
If the form attributes should be added to the request parameters. (default:
true
) body.delete-uploaded-files-on-end
-
If the uploaded files should be removed after serving the request.
body.preallocate-body-buffer
-
If the body buffer should pre-allocated based on the
Content-Length
header value. (default:1K
) auth.session.encryption-key
-
The encryption key that is used to store persistent logins.
so-reuse-port
-
Enable socket reuse port.
tcp-quick-ack
-
Enable tcp quick ack.
tcp-cork
-
Enable tcp cork.
tcp-fast-open
-
Enable tcp fast open.
domain-socket
-
Path to a unix domain socket. (default:
/var/run/io.quarkus.app.socket
) domain-socket-enabled
-
Enables domain socket.
record-request-start-time
-
If enabled then start time will be recorded to enable logging of total request time. (default:
false
) access-log.enabled
-
If access logging is enabled. (default:
false
) access-log.pattern
-
The access log pattern. (default:
common
) access-log.log-to-file
-
If logging should be done to a separate file. (default:
false
) access-log.base-file-name
-
The access log file base name. (default:
quarkus
) access-log.log-directory
-
The log directory to use when logging access to a file.
access-log.log-directory
-
The log directory to use when logging access to a file.
access-log.log-suffix
-
The log file suffix. (default:
.log
) access-log.category
-
The log category to use if logging is being done via the standard log mechanism. (default:
io.quarkus.http.access-log
) access-log.rotate
-
If the log should be rotated daily. (default:
true
) same-site-cookie.<name>.case-sensitive
-
If the cookie pattern is case sensitive.
same-site-cookie.<name>.value
-
The value to set in the samesite attribute.
same-site-cookie.<name>.enable-client-checker
-
Some User Agents break when sent SameSite=None, this will detect them and avoid sending the value. (default:
true
) same-site-cookie.<name>.add-secure-for-none
-
If this is true then the 'secure' attribute will automatically be sent on cookies with a SameSite attribute of None. (default:
true
)
If metrics extension is registered, you can enable to get HTTP metrics by setting quarkus.resteasy.metrics.enabled
to true.
JAX-RS
Quarkus uses JAX-RS to define REST-ful web APIs. Under the covers, Rest-EASY is working with Vert.X directly without using any Servlet.
It is important to know that if you want to use any feature that implies a Servlet
(ie Servlet Filters) then you need to add the quarkus-undertow
extension to switch back to the Servlet
ecosystem but generally speaking, you don’t need to add it as everything else is well-supported.
@Path("/book")
public class BookResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public List<Book> getAllBooks() {}
@POST
@Produces(MediaType.APPLICATION_JSON)
public Response createBook(Book book) {}
@DELETE
@Path("{isbn}")
@Produces(MediaType.APPLICATION_JSON)
public Response deleteBook(
@PathParam("isbn") String isbn) {}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("search")
public Response searchBook(
@QueryParam("description") String description) {}
}
To get information from request:
@PathParam
-
Gets content from request URI. (example:
/book/{id} @PathParam("id")
) @QueryParam
-
Gets query parameter. (example:
/book?desc="" @QueryParam("desc)
) @FormParam
-
Gets form parameter.
@MatrixParam
-
Get URI matrix parameter. (example:
/book;author=mkyong;country=malaysia
) @CookieParam
-
Gets cookie param by name.
@HeaderParam
-
Gets header parameter by name.
Valid HTTP method annotations provided by the spec are: @GET
, @POST
, @PUT
, @DELETE
, @PATCH
, @HEAD
and @OPTIONS
.
You can create new annotations that bind to HTTP methods not defined by the spec.
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@HttpMethod("LOCK")
public @interface LOCK {
}
@LOCK
public void lockIt() {}
}
Injecting
Using @Context
annotation to inject JAX-RS and Servlet information.
@GET
public String getBase(@Context UriInfo uriInfo) {
return uriInfo.getBaseUri();
}
Possible injectable objects: SecurityContext
, Request
, Application
, Configuration
, Providers
, ResourceContext
, ServletConfig
, ServletContext
, HttpServletRequest
, HttpServletResponse
, HttpHeaders
, Urinfo
, SseEventSink
and Sse
.
HTTP Filters
HTTP request and response can be intercepted to manipulate the metadata (ie headers, parameters, media type, …) or abort a request.
You only need to implement the next ContainerRequestFilter
and ContainerResponseFilter
JAX-RS interfaces respectively.
@Provider
public class LoggingFilter
implements ContainerRequestFilter {
@Context
UriInfo info;
@Context
HttpServletRequest request;
@Override
public void filter(ContainerRequestContext context) {
final String method = context.getMethod();
final String path = info.getPath();
final String address = request.getRemoteAddr();
System.out.println("Request %s %s from IP %s",
method, path, address);
}
}
Exception Mapper
You can map exceptions to produce a custom output by implementing ExceptionMapper
interface:
@Provider
public class ErrorMapper
implements ExceptionMapper<Exception> {
@Override
public Response toResponse(Exception exception) {
int code = 500;
if (exception instanceof WebApplicationException) {
code = ((WebApplicationException) exception)
.getResponse().getStatus();
}
return Response.status(code)
.entity(
Json.createObjectBuilder()
.add("error", exception.getMessage())
.add("code", code)
.build()
)
.build();
}
}
Caching
Annotations to set Cache-Control headers:
@Produces(MediaType.APPLICATION_JSON)
@org.jboss.resteasy.annotations.cache.NoCache
public User me() {}
@Produces(MediaType.APPLICATION_JSON)
@org.jboss.resteasy.annotations.cache.Cache(
maxAge = 2000,
noStore = false
)
public User you() {}
Vert.X Filters and Routes
Programmatically
You can also register Vert.X Filters and Router programmatically inside a CDI bean:
import io.quarkus.vertx.http.runtime.filters.Filters;
import io.vertx.ext.web.Router;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
@ApplicationScoped
public class MyBean {
public void filters(
@Observes Filters filters) {
filters
.register(
rc -> {
rc.response()
.putHeader("X-Filter", "filter 1");
rc.next();
},
10);
}
public void routes(
@Observes Router router) {
router
.get("/")
.handler(rc -> rc.response().end("OK"));
}
}
Declarative
You can use @Route
annotation to use reactive routes and @RouteFilter
to sue reactive filters in a declarative way:
./mvnw quarkus:add-extension
-Dextensions="quarkus-vertx-web"
@ApplicationScoped
public class MyDeclarativeRoutes {
@Route(path = "/hello", methods = HttpMethod.GET)
public void greetings(RoutingContext rc) {
String name = rc.request().getParam("name");
if (name == null) {
name = "world";
}
rc.response().end("hello " + name);
}
@RouteFilter(20)
void filter(RoutingContext rc) {
rc.response().putHeader("X-Filter", "filter 2");
rc.next();
}
@Route
String hello(@Param Optional<String> name) {}
@Route
String helloFromHeader(@Header("My-Header") String header) {}
@Route
String createPerson(@Body Person person) {}
}
GraphQL
Quarkus integrates with GraphQL using MicroProfile GraphQL integration.
./mvnw quarkus:add-extension
-Dextensions="graphql"
@GraphQLApi
public class FilmResource {
@Query("allFilms")
public List<String> films() {
}
@Query
public String getFilm(@Name("filmId") int id)) {}
@Query
public List<Hero> getHeroesWithSurname(
@DefaultValue("Skywalker") String surname) {
}
@Mutation
public Greetings load(Greetings greetings) {
}
}
If name not provided, then query name is resolved from method name.
You can see the full schema at /graphql/schema.graphql
.
Also GraphiQL UI is enabled at dev and test mode at /graphql-ui/
.
Extension can be configured with the follwoing paramters prefixed with quarkus.smallrye-graphql
.
root-path
-
The rootPath under which queries will be served. (default:
/graphql
) root-path-ui
-
The path where GraphQL UI is available. (default:
/graphql-ui
) always-include-ui
-
The path where GraphQL UI is available. (default:
/graphql-ui
) root-path-ui
-
Always include the UI. By default this will only be included in dev and test. (default:
false
) enable-ui
-
If GraphQL UI should be enabled. (default:
false
) schema-available
-
Make the schema available over HTTP. (default:
true
) metrics.enabled
-
Enable metrics. (default:
false
)
GraphQL Clients
./mvnw quarkus:add-extension
-Dextensions="graphql-client"
quarkus.smallrye-graphql-client.star-wars.url=https://swapi-graphql.netlify.app/.netlify/functions/index
Typesafe client
@GraphQLClientApi(configKey = "star-wars")
public interface StarWarsClientApi {
FilmConnection allFilms();
}
@Inject
StarWarsClientApi typesafeClient;
allFilms
is also the name of the query.
You can override the value using @Query(value="allStarWarsFilms")
Dynamic client
@Inject
@GraphQLClient("star-wars")
DynamicGraphQLClient dynamicClient;
Document query = document(
operation(
field("allFilms",
field("films",
field("title"),
field("planetConnection",
field("planets",
field("name")
)
)
)
)
)
);
Response response = dynamicClient.executeSync(query)
Vert.X Verticle
Vert.X Verticles are also supported:
@ApplicationScoped
public class VerticleDeployer {
@Inject
Vertx vertx;
public void init(@Observes StartupEvent ev) {
CountDownLatch latch = new CountDownLatch(1);
vertx.deployVerticle(BareVerticle::new,
new DeploymentOptions()
.setConfig(
new JsonObject()
.put("id", "bare")
)
)
.thenAccept(x -> latch.countDown());
latch.countDown();
}
}
Verticles can be:
- bare
-
extending
io.vertx.core.AbstractVerticle
. - mutiny
-
extendig
io.smallrye.mutiny.vertx.core.AbstractVerticle
.
GZip Support
You can configure Quarkus to use GZip in the application.properties
file using the next properties with quarkus.resteasy
suffix:
gzip.enabled
-
EnableGZip. (default:
false
) gzip.max-input
-
Configure the upper limit on deflated request body. (default:
10M
)
GRPC
Quarkus integrates with gRPC:
./mvnw quarkus:add-extension
-Dextensions="quarkus-grpc"
Then you need to configure build tool
with gRPC plugins. In the case of Maven, the kr.motd.maven:os-maven-plugin
extension and org.xolstice.maven.plugins:protobuf-maven-plugin
Protos files are stored at src/main/proto
.
When java
files are created two service implementations are provided: one with default gRPC API and other with Mutiny support.
With quarkus.grpc.server
prefix, the next configuration properties can be set:
port
-
The gRPC Server port. (default:
9000
) host
-
The gRPC server host. (default:
0.0.0.0
) handshake-timeout
-
The gRPC handshake timeout.
max-inbound-message-size
-
The max inbound message size in bytes.
plain-text
-
Use plain text. (default:
true
) alpn
-
TWhether ALPN should be used. (default:
true
) enable-reflection-service
-
Enables the gRPC Reflection Service. (default:
false
) ssl.certificate
-
The file path to a server certificate or certificate chain in PEM format.
ssl.key
-
The file path to the corresponding certificate private key file in PEM format.
ssl.key-store
-
An optional key store which holds the certificate information instead of specifying separate files.
ssl.key-store-type
-
An optional parameter to specify the type of the key store file.
ssl.key-store-password
-
A parameter to specify the password of the key store file. (default:
password
) ssl.trust-store
-
Trust store which holds the certificate information of the certificates to trust
ssl.trust-store-type
-
Parameter to specify type of the trust store file.
ssl.trust-store-password
-
A parameter to specify the password of the trust store file.
ssl.cipher-suites
-
A list of the cipher suites to use.
ssl.protocols
-
The list of protocols to explicitly enable. (default:
TLSv1.3,TLSv1.2
) transport-security.certificate
-
The path to the certificate file.
transport-security.key
-
The path to the private key file.
load-balancing-policy
-
Use a custom load balancing policy. Possible values:
pick_first
,round_robin
,grpclb
. (default:pick_first
)
To consume the service:
@GrpcClient("hello")
GreeterGrpc.GreeterBlockingStub client;
@GrpcClient("hello")
io.grpc.Channel channel;
// Adding headers
Metadata extraHeaders = new Metadata();
extraHeaders.put("my-header", "my-interface-value");
GreeterBlockingStub alteredClient = io.quarkus.grpc.GrpcClientUtils.attachHeaders(client, extraHeaders);
Interceptors
import io.quarkus.grpc.GlobalInterceptor;
import io.grpc.ClientInterceptor;
@GlobalInterceptor
@ApplicationScoped
public class MyInterceptor implements ClientInterceptor {}
@RegisterClientInterceptor(MySpecialInterceptor.class) (1)
@GrpcClient("helloService")
Greeter greeter;
Some configuration example to set the host and the SSL parameters:
quarkus.grpc.clients.hello.host=localhost
quarkus.grpc.clients.hello.plain-text=false
quarkus.grpc.clients.hello.ssl.certificate=src/main/resources/tls/client.pem
quarkus.grpc.clients.hello.ssl.key=src/main/resources/tls/client.key
quarkus.grpc.clients.hello.ssl.trust-store=src/main/resources/tls/ca.pem
Smallrye Stork
Stork implements client load-balancing strategies supporting Kubernetes, and Consul.
Consul
Add following dependencies:
<dependency>
<groupId>io.smallrye.stork</groupId>
<artifactId>stork-service-discovery-consul</artifactId>
</dependency>
<dependency>
<groupId>io.smallrye.reactive</groupId>
<artifactId>smallrye-mutiny-vertx-consul-client</artifactId>
</dependency>
Register service instances to Consul:
@ApplicationScoped
public class Registration {
@ConfigProperty(name = "consul.host") String host;
@ConfigProperty(name = "consul.port") int port;
public void init(@Observes StartupEvent ev, Vertx vertx) {
ConsulClient client = ConsulClient.create(vertx,
new ConsulClientOptions().setHost(host).setPort(port));
client.registerServiceAndAwait(
new ServiceOptions().setPort(8080)
.setAddress("host1").setName("my-service").setId("blue"));
client.registerServiceAndAwait(
new ServiceOptions().setPort(8080)
.setAddress("host2").setName("my-service").setId("red"));
}
}
Configuration:
consul.host=localhost
consul.port=8500
stork.my-service.service-discovery=consul
stork.my-service.service-discovery.consul-host=localhost
stork.my-service.service-discovery.consul-port=8500
stork.my-service.load-balancer=round-robin
Kubernetes
Add following dependencies:
<dependency>
<groupId>io.smallrye.reactive</groupId>
<artifactId>stork-service-discovery-kubernetes</artifactId>
</dependency>
stork.my-service.service-discovery=kubernetes
stork.my-service.service-discovery.k8s-namespace=my-namespace
stork.my-service.load-balancer=round-robin
Using Stork at client
@RegisterRestClient(baseUri = "stork://my-service")
public interface MyService {
@GET
@Produces(MediaType.TEXT_PLAIN)
String get();
}
Fault Tolerance
Quarkus uses MicroProfile Fault Tolerance spec:
./mvnw quarkus:add-extension
-Dextensions="io.quarkus:quarkus-smallrye-fault-tolerance"
MicroProfile Fault Tolerance spec uses CDI interceptor and it can be used in several elements such as CDI bean, JAX-RS resource or MicroProfile Rest Client.
To do automatic retries on a method:
@Path("/api")
@RegisterRestClient
public interface WorldClockService {
@GET @Path("/json/cet/now")
@Produces(MediaType.APPLICATION_JSON)
@Retry(maxRetries = 2)
WorldClock getNow();
}
You can set fallback code in case of an error by using @Fallback
annotation:
@Retry(maxRetries = 1)
@Fallback(fallbackMethod = "fallbackMethod")
WorldClock getNow(){}
public WorldClock fallbackMethod() {
return new WorldClock();
}
fallbackMethod
must have the same parameters and return type as the annotated method.
You can also set logic into a class that implements FallbackHandler
interface:
public class RecoverFallback
implements FallbackHandler<WorldClock> {
@Override
public WorldClock handle(ExecutionContext context) {
}
}
And set it in the annotation as value @Fallback(RecoverFallback.class)
.
In case you want to use circuit breaker pattern:
@CircuitBreaker(requestVolumeThreshold = 4,
failureRatio=0.75,
delay = 1000)
WorldClock getNow(){}
If 3 (4 x 0.75)
failures occur among the rolling window of 4 consecutive invocations then the circuit is opened for 1000 ms and then be back to half open.
If the invocation succeeds then the circuit is back to closed again.
You can use bulkahead pattern to limit the number of concurrent access to the same resource.
If the operation is synchronous it uses a semaphore approach, if it is asynchronous a thread-pool one.
When a request cannot be processed BulkheadException
is thrown.
It can be used together with any other fault tolerance annotation.
@Bulkhead(5)
@Retry(maxRetries = 4,
delay = 1000,
retryOn = BulkheadException.class)
WorldClock getNow(){}
From smallrye project Fibonacci and Exponential Backoff retries annotations are provided:
public class TwoBackoffsOnMethodService {
@Retry
@io.smallrye.faulttolerance.api.ExponentialBackoff
@io.smallrye.faulttolerance.api.FibonacciBackoff
public void hello() {
throw new IllegalArgumentException();
}
}
Fault tolerance annotations:
Annotation | Properties |
---|---|
|
|
|
|
|
|
|
|
|
|
|
You can override annotation parameters via configuration file using property [classname/methodname/]annotation/parameter
:
org.acme.quickstart.WorldClock/getNow/Retry/maxDuration=30
# Class scope
org.acme.quickstart.WorldClock/Retry/maxDuration=3000
# Global
Retry/maxDuration=3000
You can also enable/disable policies using special parameter enabled
.
org.acme.quickstart.WorldClock/getNow/Retry/enabled=false
# Disable everything except fallback
MP_Fault_Tolerance_NonFallback_Enabled=false
MicroProfile Fault Tolerance integrates with MicroProfile Metrics spec. You can disable it by setting MP_Fault_Tolerance_Metrics_Enabled to false.
|
Observability
Health Checks
Quarkus relies on MicroProfile Health spec to provide health checks.
./mvnw quarkus:add-extension
-Dextensions="io.quarkus:quarkus-smallrye-health"
By just adding this extension, an endpoint is registered to /q/health
providing a default health check.
{
"status": "UP",
"checks": [
]
}
To create a custom health check you need to implement the HealthCheck
interface and annotate either with @Readiness
(ready to process requests) or @Liveness
(is running) annotations.
@Readiness
public class DatabaseHealthCheck implements HealthCheck {
@Override
public HealthCheckResponse call() {
HealthCheckResponseBuilder responseBuilder =
HealthCheckResponse.named("Database conn");
try {
checkDatabaseConnection();
responseBuilder.withData("connection", true);
responseBuilder.up();
} catch (IOException e) {
// cannot access the database
responseBuilder.down()
.withData("error", e.getMessage());
}
return responseBuilder.build();
}
}
Builds the next output:
{
"status": "UP",
"checks": [
{
"name": "Database conn",
"status": "UP",
"data": {
"connection": true
}
}
]
}
Since health checks are CDI beans, you can do:
@ApplicationScoped
public class DatabaseHealthCheck {
@Liveness
HealthCheck check1() {
return io.smallrye.health.HealthStatus
.up("successful-live");
}
@Readiness
HealthCheck check2() {
return HealthStatus
.state("successful-read", this::isReady)
}
private boolean isReady() {}
}
You can ping liveness or readiness health checks individually by querying /q/health/live
or /q/health/ready
.
Quarkus comes with some HealthCheck
implementations for checking service status.
-
SocketHealthCheck: checks if host is reachable using a socket.
-
UrlHealthCheck: checks if host is reachable using a Http URL connection.
-
InetAddressHealthCheck: checks if host is reachable using
InetAddress.isReachable
method.
@Liveness
HealthCheck check1() {
return new UrlHealthCheck("https://www.google.com")
.name("Google-Check");
}
If you want to override or set manually readiness/liveness probes, you can do it by setting health properties:
quarkus.smallrye-health.root-path=/hello
quarkus.smallrye-health.liveness-path=/customlive
quarkus.smallrye-health.readiness-path=/customready
Automatic readiness probes
Some default readiness probes are provided by default if any of the next features are added:
- datasource
-
A probe to check database connection status.
- kafka
-
A probe to check kafka connection status. In this case you need to enable manually by setting
quarkus.kafka.health.enabled
totrue
. - mongoDB
-
A probe to check MongoDB connection status.
- neo4j
-
A probe to check Neo4J connection status.
- artemis
-
A probe to check Artemis JMS connection status.
- kafka-streams
-
Liveness (for stream state) and Readiness (topics created) probes.
- vault
-
A probe to check Vault conection status.
- gRPC
-
A readiness probe for the gRPC services.
- Cassandra
-
A readiness probe to check Cassandra connection status.
- Redis
-
A readiness probe to check Redis connection status.
You can disable the automatic generation by setting <component>.health.enabled
to false.
quarkus.datasource.myotherdatasource.health-exclude=true
quarkus.datasource.health.enabled=true
quarkus.kafka-streams.health.enabled=false
quarkus.mongodb.health.enabled=false
quarkus.neo4j.health.enabled=false
In the case of Vault you can pass parameters to modify the call of the status
endpoint in Vault.
quarkus.vault.health.enabled=true
quarkus.vault.health.stand-by-ok=true
quarkus.vault.health.performance-stand-by-ok=true
Health groups are supported to provide custom health checks groups:
@io.smallrye.health.HealthGroup("mygroup1")
public class SimpleHealthGroupCheck implements HealthCheck {
}
You can ping grouped health checks by querying /group/mygroup1
.
Group root path can be configured:
quarkus.smallrye-health.group-path=/customgroup
MicroProfile Health currently doesn’t support returning reactive types, but SmallRye Health does.
Implement the io.smallrye.health.api.AsyncHealthCheck
interface instead of the org.eclipse.microprofile.health.HealthCheck
.
@Liveness
@ApplicationScoped
public class LivenessAsync implements AsyncHealthCheck {
@Override
public Uni<HealthCheckResponse> call() {
return Uni.createFrom().item(HealthCheckResponse.up("liveness-reactive"))
.onItem().delayIt().by(Duration.ofMillis(10));
}
}
Metrics
Quarkus can utilize the MicroProfile Metrics spec to provide metrics support.
./mvnw quarkus:add-extension
-Dextensions="io.quarkus:quarkus-smallrye-metrics"
The metrics can be read with JSON or the OpenMetrics format.
An endpoint is registered automatically at /q/metrics
providing default metrics.
MicroProfile Metrics annotations:
@Timed
-
Tracks the duration.
@SimplyTimed
-
Tracks the duration without mean and distribution calculations.
@Metered
-
Tracks the frequency of invocations.
@Counted
-
Counts number of invocations.
@Gauge
-
Samples the value of the annotated object.
@ConcurrentGauge
-
Gauge to count parallel invocations.
@Metric
-
Used to inject a metric. Valid types
Meter
,Timer
,Counter
,Histogram
.Gauge
only on producer methods/fields.
@GET
//...
@Timed(name = "checksTimer",
description = "A measure of how long it takes
to perform a hello.",
unit = MetricUnits.MILLISECONDS)
public String hello() {}
@Counted(name = "countWelcome",
description = "How many welcome have been performed.")
public String hello() {}
@Gauge
annotation returning a measure as a gauge.
@Gauge(name = "hottestSauce", unit = MetricUnits.NONE,
description = "Hottest Sauce so far.")
public Long hottestSauce() {}
Injecting a histogram using @Metric
.
@Inject
@Metric(name = "histogram")
Histogram historgram;
You can configure Metrics:
quarkus.smallrye-metrics.path=/mymetrics
Prefix is quarkus.smallrye-metrics
.
path
-
The path to the metrics handler. (default:
/q/metrics
) extensions.enabled
-
Metrics are enabled or not. (default:
true
) micrometer.compatibility
-
Apply Micrometer compatibility mode. (default:
false
)
quarkus.hibernate-orm.metrics.enabled
set to true
exposes Hibernate metrics under vendor
scope.
quarkus.mongodb.metrics.enabled
set to true
exposes MongoDB metrics under vendor
scope.
You can apply metrics annotations via CDI stereotypes:
@Stereotype
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@Timed(name = "checksTimer",
description = "A measure of how long it takes
to perform a hello.",
unit = MetricUnits.MILLISECONDS)
public @interface TimedMilliseconds {
}
There is a tight integration with Micrometer in the form of an extension:
./mvnw quarkus:add-extension
-Dextensions="micrometer"
Add a micrometer dependency for the registry of your choosing:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
You can configure Micrometer.
Prefix is quarkus.micrometer
:
enabled
-
Micrometer metrics support. (default:
true
) registry-enabled-default
-
Micrometer MeterRegistry discovery. (default:
true
) binder-enabled-default
-
Micrometer MeterBinder discovery. (default:
true
) binder.vertx.enabled
-
Vert.x metrics support.
binder.mp-metrics.enabled
-
Microprofile Metrics support.
binder.jvm
-
Micrometer JVM metrics support. (default:
true
) binder.system
-
Micrometer System metrics support. (default:
true
) export.datadog.enabled
-
Support for export to Datadog Support for Datadog.
export.jmx.enabled
-
Support for export to JMX Support for JMX.
export.prometheus.enabled
-
Support for export to Prometheus.
export.prometheus.path
-
The path for the prometheus metrics endpoint (produces text/plain). (default:
/q/metrics
) export.azuremonitor.enabled
-
Support for export to Azure Monitor.
export.azuremonitor.instrumentation-key
-
The path for the azure monitor instrumentationKey.
export.statsd.enabled
-
Support for export to StatsD.
export.stackdriver.enabled
-
Micrometer metrics support. (default:
true
) export.signalfx.enabled
-
Micrometer metrics support. (default:
true
) export.signalfx.uri
-
Signalfx URI.
export.signalfx.access-token
-
Access Token.
binder.vertx.match-patterns
-
Comma-separated case-sensitive list of regular expressions defining Paths that should be matched and used as tags
binder.vertx.ignore-patterns
-
Comma-separated case-sensitive list of regular expressions defining Paths that should be ignored / not measured.
export.datadog
-
Datadog MeterRegistry configuration in
Map<String, String>
format. export.jmx
-
JMX registry configuration properties in
Map<String, String>
format. export.prometheus
-
Prometheus registry configuration properties in
Map<String, String>
format. export.stackdriver
-
Stackdriver registry configuration properties in
Map<String, String>
format.
Tracing
Quarkus can utilize the MicroProfile OpenTracing spec.
./mvnw quarkus:add-extension
-Dextensions="io.quarkus:quarkus-smallrye-opentracing"
Requests sent to any endpoint are traced automatically.
This extension includes OpenTracing support and Jaeger
tracer.
Jaeger tracer configuration:
quarkus.jaeger.service-name=myservice
quarkus.jaeger.sampler-type=const
quarkus.jaeger.sampler-param=1
quarkus.jaeger.endpoint=http://localhost:14268/api/traces
quarkus.jaeger.metrics.enabled=true
@Traced
annotation can be set to disable tracing at class or method level.
Tracer
class can be injected into the class.
@Inject
Tracer tracer;
tracer.activeSpan().setBaggageItem("key", "value");
You can disable Jaeger
extension by using quarkus.jaeger.enabled
property.
You can log the traceId
, spanId
and sampled
in normal log:
quarkus.log.console.format=%d{HH:mm:ss} %-5p traceId=%X{traceId},
spanId=%X{spanId}, sampled=%X{sampled} [%c{2.}] (%t) %s%e%n
Additional tracers
JDBC Tracer
Adds a span for each JDBC queries.
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-jdbc</artifactId>
</dependency>
Configure JDBC driver apart from tracing properties seen before:
# add ':tracing' to your database URL
quarkus.datasource.url=
jdbc:tracing:postgresql://localhost:5432/mydatabase
quarkus.datasource.driver=
io.opentracing.contrib.jdbc.TracingDriver
quarkus.hibernate-orm.dialect=
org.hibernate.dialect.PostgreSQLDialect
OpenTelemetry
./mvnw quarkus:add-extension
-Dextensions="opentelemetry-otlp-exporter"
Possible configuration:
quarkus.application.name=myservice
quarkus.opentelemetry.enabled=true
quarkus.opentelemetry.tracer.exporter.otlp.endpoint=http://localhost:4317
quarkus.opentelemetry.tracer.exporter.otlp.headers=Authorization=Bearer my_secret
The OpenTelemetry extension enables the W3C Trace Context and the W3C Baggage propagators, you can however choose any of the supported OpenTelemetry propagators.
The b3
, b3multi
, jaeger
and ottrace
propagators:
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-extension-trace-propagators</artifactId>
</dependency>
The xray
propagator:
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-extension-aws</artifactId>
</dependency>
You can configure OpenTelemetry.
Prefix is quarkus.opentelemetry
:
enabled
-
OpenTelemetry support (default:
true
) propagators
-
Comma separated list of OpenTelemetry propagators which must be supported. Possible values b3, b3multi, baggage, jaeger, ottrace, tracecontext, xray (default:
traceContext
,baggage
). tracer.enabled
-
Support for tracing with OpenTelemetry.
tracer.resource-attributes
-
A comma separated list of name=value resource attributes that represents the entity producing telemetry
tracer.sampler
-
The sampler to use for tracing. Possible values
off
,on
,ratio
. (Default:on
) tracer.sampler.ratio
-
Ratio to sample
tracer.sampler.parent-based
-
If the sampler to use for tracing is parent based (default:
true
) tracer.suppress-non-application-uris
-
Suppress non-application uris from trace collection. (default:
true
) tracer.exporter.otlp.enabled
-
OTLP SpanExporter support (default:
true
) - tracer.exporter.otlp.endpoint
-
The OTLP endpoint to connect to
tracer.exporter.otlp.headers
-
Key-value pairs to be used as headers associated with gRPC requests
tracer.exporter.otlp.export-timeout
-
The maximum amount of time to wait for the collector to process exported spans before an exception is thrown. (default:
10S
) tracer.exporter.otlp.compression
-
Compression method to be used by exporter to compress the payload
Kafka Tracer
Adds a span for each message sent to or received from a Kafka topic.
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-kafka-client<</artifactId>
</dependency>
And configure it:
...
mp.messaging.outgoing.generated-price.topic=prices
# For Produces
mp.messaging.outgoing.generated-price.interceptor.classes=io.opentracing.contrib.kafka.TracingProducerInterceptor
...
# For consumers
mp.messaging.incoming.prices.interceptor.classes=io.opentracing.contrib.kafka.TracingConsumerInterceptor
AWS XRay
If you are building native images, and want to use AWS X-Ray Tracing with your lambda you will need to include quarkus-amazon-lambda-xray
as a dependency in your pom.
Native Executable
You can build a native image by using GraalVM. The common use case is creating a Docker image so you can execute the next commands:
./mvnw package -Pnative -Dquarkus.native.container-build=true
docker build -f src/main/docker/Dockerfile.native
-t quarkus/getting-started .
docker run -i --rm -p 8080:8080 quarkus/getting-started
You can use quarkus.native.container-runtime
to select the container runtime to use.
Now docker
(default) and podman
are the valid options.
./mvnw package -Pnative -Dquarkus.native.container-runtime=podman
To configure native application, you can create a config
directory at the same place as the native file and place an application.properties
file inside. config/application.properties
.
To produce a native executable with native excutable use -Dquarkus.native.additional-build-args=-J—enable-preview
.
SSL
To create a native image with SSL you need to copy SunEC library and certificates:
Java 8:
FROM quay.io/quarkus/ubi-quarkus-native-image:{graalvm-version}-java8 as nativebuilder
RUN mkdir -p /tmp/ssl-libs/lib \
&& cp /opt/graalvm/jre/lib/security/cacerts /tmp/ssl-libs \
&& cp /opt/graalvm/jre/lib/amd64/libsunec.so /tmp/ssl-libs/lib/
FROM registry.access.redhat.com/ubi8/ubi-minimal
WORKDIR /work/
COPY --from=nativebuilder /tmp/ssl-libs/ /work/
COPY target/*-runner /work/application
RUN chmod 775 /work /work/application
EXPOSE 8080
CMD ["./application", "-Dquarkus.http.host=0.0.0.0", "-Djava.library.path=/work/lib", "-Djavax.net.ssl.trustStore=/work/cacerts"]
Java 11:
FROM quay.io/quarkus/ubi-quarkus-native-image:{graalvm-version}-java11 as nativebuilder
RUN mkdir -p /tmp/ssl-libs/lib \
&& cp /opt/graalvm/lib/security/cacerts /tmp/ssl-libs \
&& cp /opt/graalvm/lib/libsunec.so /tmp/ssl-libs/lib/
FROM registry.access.redhat.com/ubi8/ubi-minimal
WORKDIR /work/
COPY --from=nativebuilder /tmp/ssl-libs/ /work/
COPY target/*-runner /work/application
RUN chmod 775 /work /work/application
EXPOSE 8080
CMD ["./application", "-Dquarkus.http.host=0.0.0.0", "-Djava.library.path=/work/lib", "-Djavax.net.ssl.trustStore=/work/cacerts"]
Inclusion of resources
By default, no resources are included in native executable.
quarkus.native.resources.includes
allows to set glob expressions to include resources based on src/main/resources
path.
Given src/main/resources/foo/selected.png
:
quarkus.native.resources.includes=foo/**
Exclusion of resources
By default, no resources are excluded.
quarkus.native.resources.excludes = foo/**
Native configuration properties prefixed with quarkus.native
:
additional-build-args
-
Additional arguments to pass to the build process.
enable-http-url-handler
-
If the HTTP url handler should be enabled. (default:
true
) enable-https-url-handler
-
If the HTTPS url handler should be enabled.
enable-all-security-services
-
If all security services should be added to the native image.
user-language
-
Defines the user language used for building the native executable.
user-country
-
Defines the user country used for building the native executable.
file-encoding
-
Defines the file encoding.
add-all-charsets
-
If all character sets should be added to the native image.
graalvm-home
-
The location of the Graal distribution.
java-home
-
The location of the JDK.
native-image-xmx
-
The maximum Java heap to be used during the native image generation.
debug-build-process
-
If the native image build should wait for a debugger to be attached before running.
publish-debug-build-process-port
-
If the debug port should be published when building with docker and debug-build-process. (default:
true
) cleanup-server
-
If the native image server should be restarted.
enable-isolates
-
If isolates should be enabled. (default:
true
) enable-fallback-images
-
If a JVM based 'fallback image' should be created if native image fails.
enable-server
-
If the native image server should be used.
auto-service-loader-registration
-
If all META-INF/services entries should be automatically registered.
dump-proxies
-
If the bytecode of all proxies should be dumped for inspection.
container-build
-
If this build should be done using a container runtime.
remote-container-build
-
If this build is done using a remote docker daemon.
builder-image
-
The docker image to use to do the image build.
container-runtime
-
The container runtime that is used to do an image based build. (
docker
,podman
) container-runtime-options
-
Options to pass to the container runtime.
enable-vm-inspection
-
If the resulting image should allow VM introspection.
full-stack-traces
-
If full stack traces are enabled in the resulting image.
enable-reports
-
If the reports on call paths and included packages/classes/methods should be generated.
report-exception-stack-traces
-
If exceptions should be reported with a full stack trace.
report-errors-at-runtime
-
If errors should be reported at runtime.
enable-dashboard-dump
-
Generate the report files for GraalVM Dashboard.
resources.includes
-
A comma separated list of globs to match resource paths that should be added to the native image.
resources.excludes
-
A comma separated list of globs to match resource paths that should exclude to the native image.
debug.enabled
-
If debug is enabled and debug symbols are generated.
Container Images Creation
You can levarage to Quarkus to generation and release of Docker containers. It provides several extensions to make it so.
mvn clean package
-Dquarkus.container-image.build=true
-Dquarkus.container-image.push=true
-Dquarkus.container-image.registry=quay.io
Prefix is quarkus.container-image
:
group
-
The group/repository of the image. (default: the
${user.name}
) name
-
The name of the image. (default: the application name)
tag
-
The tag of the image. (default: the application version)
additional-tags
-
Additional tags of the container image.
registry
-
The registry to use for pushing. (default:
docker.io
) username
-
The registry username.
password
-
The registry password.
insecure
-
Flag to allow insecure registries. (default:
false
) build
-
Boolean to set if image should be built. (default:
false
) push
-
Boolean to set if image should be pushed. (default:
false
) labels
-
Custom labels to add to the generated image.
Jib
./mvnw quarkus:add-extensions
-Dextensions="quarkus-container-image-jib"
Quarkus copies any file under src/main/jib
into the built container image.
Prefix is quarkus.container-image-jib
:
base-jvm-image
-
The base image to use for the jib build. (default:
fabric8/java-alpine-openjdk8-jre
) base-native-image
-
The base image to use for the native build. (default:
registry.access.redhat.com/ubi8/ubi-minimal
) jvm-arguments
-
The arguments to pass to java. (default:
-Dquarkus.http.host=0.0.0.0,-Djava.util.logging.manager=org.jboss.logmanager.LogManager
) native-arguments
-
The arguments to pass to the native application. (default:
-Dquarkus.http.host=0.0.0.0
) environment-variables
-
Map of environment variables.
jvm-entrypoint
-
A custom entry point of the container image in JVM mode.
native-entrypoint
-
A custom entry point of the container image in native mode.
offline-mode
-
Whether or not to operate offline. (default:
false
) base-registry-username
-
The username to use to authenticate with the registry used to pull the base JVM image.
base-registry-password
-
The password to use to authenticate with the registry used to pull the base JVM image.
ports
-
The ports to expose.
user
-
The user to use in generated image.
always-cache-base-image
-
Controls the optimization which skips downloading base image layers that exist in a target registry (default:
false
). appcds-builder-image
-
When AppCDS generation is enabled, if this property is set, then the JVM used to generate the AppCDS file will be the JVM present in the container image.
platforms
-
List of target platforms. (ie
linux/amd64
). docker-executable-ame
-
Name of binary used to execute the docker commands.
Docker
./mvnw quarkus:add-extensions
-Dextensions="quarkus-container-image-docker"
Prefix is quarkus.container-image-s2i
:
dockerfile-jvm-path
-
Path to the JVM Dockerfile. (default:
${project.root}/src/main/docker/Dockerfile.jvm
) dockerfile-native-path
-
Path to the native Dockerfile. (default:
${project.root}/src/main/docker/Dockerfile.native
)
Buildpacks
./mvnw quarkus:add-extensions
-Dextensions="quarkus-container-image-buildpack"
Prefix is quarkus.buildpack
:
jvm-builder-image
-
The buildpacks builder image to use when building the project in jvm mode.
native-builder-image
-
The buildpacks builder image to use when building the project in native mode.
builder-env
-
Environment key/values to pass to buildpacks.
run-image
-
The buildpacks run image to use when building the project.
pull-timeout-seconds
-
Max pull timeout for builder/run images, in seconds (default:
300
) docker-host
-
DOCKER_HOST value to use.
log-level
-
Log level to use. (default
info
) base-registry-username
-
The username to use to authenticate with the registry used to pull the base image.
base-registry-password
-
The password to use to authenticate with the registry used to pull the base image.
S2I
./mvnw quarkus:add-extensions
-Dextensions="quarkus-container-image-s2i"
Prefix is quarkus.container-image-docker
:
base-jvm-image
-
The base image to use for the s2i build. (default:
fabric8/java-alpine-openjdk8-jre
) base-native-image
-
The base image to use for the native build. (default:
registry.access.redhat.com/ubi8/ubi-minimal
)
Kubernetes
Quarkus can use Dekorate to generate Kubernetes resources.
./mvnw quarkus:add-extensions
-Dextensions="quarkus-kubernetes"
Running ./mvnw package
the Kubernetes resources are created at target/kubernetes/
directory.
Container Images Creation integrates with Kubernetes extension, so no need of extra Kubernetes properties. |
Generated resource is integrated with MicroProfile Health annotations.
Also, you can customize the generated resource by setting the new values in application.properties
:
quarkus.kubernetes.namespace=mynamespace
quarkus.kubernetes.replicas=3
quarkus.kubernetes.labels.foo=bar
quarkus.kubernetes.readiness-probe.period-seconds=45
quarkus.kubernetes.mounts.github-token.path=/deployment/github
quarkus.kubernetes.mounts.github-token.read-only=true
quarkus.kubernetes.secret-volumes.github-token.volume-name=github-token
quarkus.kubernetes.secret-volumes.github-token.secret-name=greeting-security
quarkus.kubernetes.secret-volumes.github-token.default-mode=420
quarkus.kubernetes.config-map-volumes.github-token.config-map-name=my-secret
quarkus.kubernetes.expose=true
quarkus.kubernetes.ingress.expose=true
quarkus.kubernetes.ingress.host=example.com
quarkus.kubernetes.env.vars.my-env-var=foobar
quarkus.kubernetes.env.configmaps=my-config-map,another-config-map
quarkus.kubernetes.env.secrets=my-secret,my-other-secret
quarkus.kubernetes.resources.requests.memory=64Mi
quarkus.kubernetes.resources.requests.cpu=250m
quarkus.kubernetes.resources.limits.memory=512Mi
quarkus.kubernetes.resources.limits.cpu=1000m
All possible values are explained at https://quarkus.io/guides/kubernetes#configuration-options.
Labels and Annotations
The generated manifest use the Kubernetes recommended labels and annotations.
"labels" : {
"app.kubernetes.io/part-of" : "todo-app",
"app.kubernetes.io/name" : "todo-rest",
"app.kubernetes.io/version" : "1.0-rc.1"
}
"annotations": {
"app.quarkus.io/vcs-url" : "<some url>",
"app.quarkus.io/commit-id" : "<some git SHA>",
}
You can override the labels by using the next properties:
quarkus.kubernetes.part-of=todo-app
quarkus.kubernetes.name=todo-rest
quarkus.kubernetes.version=1.0-rc.1
Or add new labels and/or annotations:
quarkus.kubernetes.labels.foo=bar
quarkus.kubernetes.annotations.foo=bar
metrics
When using metrics extension, Prometheus annotations are generated:
prometheus.io/scrape: "true"
prometheus.io/path: /metrics
prometheus.io/port: "8080"
Kubernetes Deployment Targets
You can generate different resources setting the property quarkus.kubernetes.deployment-target
.
Possible values are kubernetes
, openshift
and knative
.
The default value is kubernetes
.
Knative Properties
Most of the Kubernetes properties are valid in Knative output by just changing the kubernetes
prefix to knative
prefix (ie quarkus.kubernetes.readiness-probe.period-seconds
to quarkus.knative.readiness-probe.period-seconds
).
There are also specific properties for Knative:
quarkus.kubernetes.deployment-target=knative
quarkus.knative.revision-name=my-revision
quarkus.knative.traffic.my-revision.percentage=80
List of configuration options:
Using Existing Resources
You an provide your Kubernetes resources in form of yaml/json and they will provide additional resources or be used as base for the generation process:
Resources are added in src/main/kubernetes
directory with the target name (kubernetes.json
, openshift.json
, knative.json
, or the yml equivalents) with one orm ore Kubernetes resources.
Any resource found will be added in the generated manifests.
If one of the provided resources has the same name as one of the generated ones, then the generated resource will be created on top of the provided resource, respecting existing content.
To override the name of the generated resource you can use: quarkus.kubernetes.name
, quarkus.openshift.name
and quarkus.knative.name
.
Deployment
To deploy automatically the generated resources, you need to set quarkus.container.deploy
flag to true
.
mvn clean package -Dquarkus.kubernetes.deploy=true
If you set this flag to true , the build and push flags from container-image are set to true too.
|
To deploy the application, the extension uses the https://github.com/fabric8io/kubernetes-client. All options described at Kubernetes Client are valid here.
Kubernetes Service Binding
Quarkus supports binding services to applications via the Service Binding Specification for Kubernetes.
The following Quarkus extensions support this feature:
-
quarkus-jdbc-mariadb
-
quarkus-jdbc-mssql
-
quarkus-jdbc-mysql
-
quarkus-jdbc-postgresql
-
quarkus-kafka-client
-
quarkus-smallrye-reactive-messaging-kafka
-
quarkus-mongodb
./mvnw quarkus:add-extensions
-Dextensions="quarkus-kubernetes-service-binding"
Datasource configuration part is absent as it is auto-discovered by Quarkus.
Minikube
Quarkus has a Minikube extension which creates Kubernetes manifests that are tailored for Minikube.
./mvnw quarkus:add-extension
-Dextensions="minikube"
Remember to execute eval $(minikube -p minikube docker-env) to build images directly inside Minkube cluster.
|
OpenShift
Instead of adding Kubernetes extension, set container image s2i and the target
to openshift
for working with OpenShift, an extension grouping all of the is created:
./mvnw quarkus:add-extension
-Dextensions="openshift"
Kubernetes Configuration Extension
Integration between MicroProfile Config spec and ConfigMaps & Secrets:
./mvnw quarkus:add-extensions
-Dextensions="quarkus-kubernetes-config"
quarkus.kubernetes-config.enabled=true
quarkus.kubernetes-config.config-maps=cmap1,cmap2
@ConfigProperty(name = "some.prop1")
String someProp1;
@ConfigProperty(name = "some.prop2")
String someProp2;
If the config key is a Quarkus configuration file application.properties
/application.yaml
, the content is parsed and each key of the configuration file is used as config property.
List of Kubernetes Config parameters.
quarkus.kubernetes-config
as prefix is skipped in the next table.
enabled
-
The application will attempt to look up the configuration from the API server. (default:
false
) fail-on-missing-config
-
The application will not start if any of the configured config sources cannot be located. (default:
true
) config-maps
-
ConfigMaps to look for in the namespace that the Kubernetes Client has been configured for. Supports CSV.
namespace
-
Access to ConfigMaps from a specific namespace.
secrets.enabled
-
Whether or not configuration can be read from secrets. (default:
false
)
Kubernetes Client
Quarkus integrates with Fabric8 Kubernetes Client.
./mvnw quarkus:add-extension
-Dextensions="quarkus-kubernetes-client"
List of Kubernetes client parameters.
quarkus.kubernetes-client
as prefix is skipped in the next table.
trust-certs
-
Trust self-signed certificates. (default:
false
) master-url
-
URL of Kubernetes API server.
namesapce
-
Default namespace.
ca-cert-file
-
CA certificate data.
client-cert-file
-
Client certificate file.
client-cert-data
-
Client certificate data.
client-key-data
-
Client key data.
client-key-algorithm
-
Client key algorithm.
client-key-passphrase
-
Client key passphrase.
username
-
Username.
password
-
Password.
watch-reconnect-interval
-
Watch reconnect interval. (default:
PT1S
) watch-reconnect-limit
-
Maximum reconnect attempts. (default:
-1
) connection-timeout
-
Maximum amount of time to wait for a connection. (default:
PT10S
) request-timeout
-
Maximum amount of time to wait for a request. (default:
PT10S
) rolling-timeout
-
Maximum amount of time to wait for a rollout. (default:
PT15M
) http-proxy
-
HTTP proxy used to access the Kubernetes.
https-proxy
-
HTTPS proxy used to access the Kubernetes.
proxy-username
-
Proxy username.
proxy-password
-
Proxy password.
no-proxy
-
IP addresses or hosts to exclude from proxying
Or programmatically:
@Dependent
public class KubernetesClientProducer {
@Produces
public KubernetesClient kubernetesClient() {
Config config = new ConfigBuilder()
.withMasterUrl("https://mymaster.com")
.build();
return new DefaultKubernetesClient(config);
}
}
And inject it on code:
@Inject
KubernetesClient client;
ServiceList myServices = client.services().list();
Service myservice = client.services()
.inNamespace("default")
.withName("myservice")
.get();
CustomResourceDefinitionList crds = client
.customResourceDefinitions()
.list();
dummyCRD = new CustomResourceDefinitionBuilder()
...
.build()
client.customResourceDefinitions()
.create(dummyCRD);
Testing
Quarkus provides a Kubernetes Mock test resource that starts a mock of Kubernetes API server and sets the proper environment variables needed by Kubernetes Client.
Register next dependency: io.quarkus:quarkus-test-kubernetes-client:test
.
@QuarkusTestResource(KubernetesMockServerTestResource.class)
@QuarkusTest
public class KubernetesClientTest {
@MockServer
private KubernetesMockServer mockServer;
@Test
public void test() {
final Pod pod1 = ...
mockServer
.expect()
.get()
.withPath("/api/v1/namespaces/test/pods")
.andReturn(200,
new PodListBuilder()
.withNewMetadata()
.withResourceVersion("1")
.endMetadata()
.withItems(pod1, pod2)
.build())
.always();
}
}
AWS Lambda
Quarkus integrates with Amazon Lambda.
./mvnw quarkus:add-extension
-Dextensions="io.quarkus:quarkus-amazon-lambda"
And then implement com.amazonaws.services.lambda.runtime.RequestHandler
interface.
public class TestLambda
implements RequestHandler<MyInput, MyOutput> {
@Override
public MyInput handleRequest(MyOutput input,
Context context) {
}
}
The interface com.amazonaws.services.lambda.runtime.RequestStreamHandler
is also supported.
The interface com.amazon.ask.SkillStreamHandler
is also supported.
You can set the handler name by using quarkus.lambda.handler
property or by annotating the Lambda with the CDI @Named
annotation.
Test
You can write tests for Amazon lambdas:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-amazon-lambda</artifactId>
<scope>test</scope>
</dependency>
@Test
public void testLambda() {
MyInput in = new MyInput();
in.setGreeting("Hello");
in.setName("Stu");
MyOutput out = LambdaClient.invoke(MyOutput.class, in);
}
To scaffold a AWS Lambda run:
mvn archetype:generate \
-DarchetypeGroupId=io.quarkus \
-DarchetypeArtifactId=quarkus-amazon-lambda-archetype \
-DarchetypeVersion={version}
Azure Functions
Quarkus can make a microservice be deployable to the Azure Functions.
To scaffold a deployable microservice to the Azure Functions run:
mvn archetype:generate \
-DarchetypeGroupId=io.quarkus \
-DarchetypeArtifactId=quarkus-azure-functions-http-archetype \
-DarchetypeVersion={version}
Funqy
Quarkus Funqy is part of Quarkus’s serverless strategy and aims to provide a portable Java API to write functions deployable to various FaaS environments like AWS Lambda, Azure Functions, Knative, and Knative events.
public class GreetingFunction {
@Inject
GreetingService service;
@io.quarkus.funqy.Funq
public String greet(String name) {}
@io.quarkus.funqy.Funq("HelloCustomer")
public String greet(Customer name) {}
@Funq
public Greeting greet(Friend friend,
@io.quarkus.funqy.Context AwsContext ctx) {}
}
In case of Amazon Lambda, only one Funqy function can be exported per Amazon Lambda deployment.
If there is only one method annotated with @Funq
then no prob, if not, you need to set the function name with quarkus.funqy.export
property.
Funqy HTTP
You can invoke on Funqy functions in a pure HTTP environment with simple adding the Funqy HTTP extension.
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-funqy-http</artifactId>
</dependency>
Funqy Cloud Events
Add the extension:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-funqy-knative-events</artifactId>
</dependency>
@Funq
public String defaultChain(String input) {}
The Cloud Event type that triggers the function is defaultChain
.
It generates a response that triggers a new Cloud Event whose type is defaultChain.output
and the event source is defaultChain
.
It can be changed by using the next properties:
quarkus.funqy.knative-events.mapping.defaultChain.trigger=configChain.output
quarkus.funqy.knative-events.mapping.defaultChain.response-type=annotated
quarkus.funqy.knative-events.mapping.defaultChain.response-source=configChain
The properties are of form: quarkus.funqy.knative-events.mapping.{function name}.
.
Also can be overriden with @io.quarkus.funqy.knative.events.CloudEventMapping
annotation.
@Funq
@CloudEventMapping(trigger = "annotated", responseSource = "annotated", responseType = "lastChainLink")
public String annotatedChain(String input) {}
responseType
chains annotatedChain
response to lastChainLink
function.
@Funq
public void lastChainLink(String input,
@Context io.quarkus.funqy.knative.events.CloudEvent event) {}
A K-Native Trigger looks like:
apiVersion: eventing.knative.dev/v1alpha1
kind: Trigger
metadata:
name: defaultchain
spec:
filter:
attributes:
type: defaultChain
subscriber:
ref:
apiVersion: serving.knative.dev/v1
kind: Service
name: funqy-knative-events-quickstart
And to curl
from inside the Kubernetes cluster:
curl -v "http://default-broker.knativetutorial.svc.cluster.local" \
-X POST \
-H "Ce-Id: 1234" \
-H "Ce-Specversion: 1.0" \
-H "Ce-Type: defaultChain" \
-H "Ce-Source: curl" \
-H "Content-Type: application/json" \
-d '"Start"'
Apache Camel
Apache Camel Quarkus has its own site: https://github.com/apache/camel-quarkus
WebSockets
Quarkus can be used to handling web sockets.
./mvnw quarkus:add-extension
-Dextensions="quarkus-websockets"
If you only want the client you can include quarkus-websockets-client
.
And web sockets classes can be used:
@ServerEndpoint("/chat/{username}")
@ApplicationScoped
public class ChatSocket {
@OnOpen
public void onOpen(Session session,
@PathParam("username") String username) {}
@OnClose
public void onClose(..) {}
@OnError
public void onError(..., Throwable throwable) {}
@OnMessage
public void onMessage(...) {}
}
OpenAPI
Quarkus can expose its API description as OpenAPI spec and test it using Swagger UI.
./mvnw quarkus:add-extension
-Dextensions="io.quarkus:quarkus-smallrye-openapi"
Then you only need to access to /openapi
to get OpenAPI v3 spec of services.
You can update the OpenApi path by setting quarkus.smallrye-openapi.path
property.
Also, in case of starting Quarkus application in dev
or test
mode, Swagger UI is accessible at /swagger-ui
.
If you want to use it in production mode you need to set quarkus.swagger-ui.always-include
property to true
.
You can update the Swagger UI path by setting quarkus.swagger-ui.path
property.
quarkus.swagger-ui.path=/my-custom-path
Possible Swagger UI options with quarkus.swagger-ui
prefix:
urls
-
The urls that will be included as options. (ie
quarkus.swagger-ui.urls.petstore=https://petstore.swagger.io/v2/swagger.json
) urls-primary-name
-
If urls option is used, this will be the name of the default selection.
title
-
The html title for the page.
theme
-
Swagger UI theme to be used.
footer
-
A footer for the html page. Nothing by default.
deep-linking
-
Enables deep linking for tags and operations.
display-operation-id
-
Controls the display of operationId in operations list.
default-models-expand-depth
-
The default expansion depth for models.
default-model-expand-depth
-
The default expansion depth for the model on the model-example section.
default-model-rendering
-
Controls how the model is shown when the API is first rendered.
display-request-duration
-
Controls the display of the request duration (in milliseconds) for "Try it out" requests.
doc-expansion
-
Controls the default expansion setting for the operations and tags.
filter
-
Enables filtering.
max-displayed-tags
-
Limits the number of tagged operations displayed to at most this many.
operations-sorter
-
Apply a sort to the operation list of each API. (
alpha
,method
,function
) show-extensions
-
Controls the display of vendor extension.
show-common-extensions
-
Controls the display of extensions.
tag-sorter
-
Apply a sort to the tag list of each API.
on-complete
-
Provides a mechanism to be notified when Swagger UI has finished rendering a newly provided definition.
syntax-highlight
-
Set to false to deactivate syntax highlighting of payloads and cURL command.
oauth2-redirect-url
-
OAuth redirect URL.
request-interceptor
-
Function to intercept remote definition, "Try it out", and OAuth 2.0 requests.
request-curl-options
-
Array of command line options available to the curl command.
response-interceptor
-
Function to intercept remote definition, "Try it out", and OAuth 2.0 responses.
show-mutated-request
-
Uses the mutated request returned from a requestInterceptor to produce the curl command in the UI.
supported-submit-methods
-
List of HTTP methods that have the "Try it out" feature enabled.
validator-url
-
Swagger UI attempts to validate specs against swagger.io’s online validator.
with-credentials
-
Enables passing credentials, as defined in the Fetch standard, in CORS requests that are sent by the browser.
model-property-macro
-
Function to set default values to each property in model.
parameter-macro
-
Function to set default value to parameters.
persist-authorization
-
It persists authorization data and it would not be lost on browser close/refresh.
layout
-
The name of a component available via the plugin system to use as the top-level layout for Swagger UI.
plugins
-
A list of plugin functions to use in Swagger UI.
presets
-
A list of presets to use in Swagger UI.
You can customize the output by using Open API v3 annotations.
@Schema(name="Developers",
description="POJO that represents a developer.")
public class Developer {
@Schema(required = true, example = "Alex")
private String name;
}
@POST
@Path("/developer")
@Operation(summary = "Create deeloper",
description = "Only be done by admin.")
public Response createDeveloper(
@RequestBody(description = "Developer object",
required = true,
content = @Content(schema =
@Schema(implementation = Developer.class)))
Developer developer)
All possible annotations can be seen at org.eclipse.microprofile.openapi.annotations package.
You can also serve OpenAPI Schema from static files instead of dynamically generated from annotation scanning.
You need to put OpenAPIdocumentation under META-INF
directory (ie: META-INF/openapi.yaml
).
A request to /openapi
will serve the combined OpenAPI document from the static file and the generated from annotations.
You can disable the scanning documents by adding the next configuration property: mp.openapi.scan.disable=true
.
Other valid document paths are: META-INF/openapi.yml
, META-INF/openapi.json
, WEB-INF/classes/META-INF/openapi.yml
, WEB-INF/classes/META-INF/openapi.yaml
, WEB-INF/classes/META-INF/openapi.json
.
You can store generated OpenAPI schema if quarkus.swagger-ui.store-schema-directory
is set.
Possible OpenAPI options with quarkus.smallrye-openapi
prefix:
path
-
The path at which to register the OpenAPI Servlet. (default:
openapi
) store-schema-directory
-
The generated OpenAPI schema documents will be stored here on build.
always-run-filter
-
Do not run the filter only at startup, but every time the document is requested (dynamic). (default:
false
) ignore-static-document
-
Do not include the provided static openapi document. (default:
false
) additional-docs-directory
-
A list of local directories that should be scanned for yaml and/or json files to be included in the static model.
security-scheme
-
Add a certain SecurityScheme with config. (
basic
,jwt
,oidc
,oauth2Implicit
) security-scheme-name
-
Add a Security Scheme name to the generated OpenAPI document. (default:
SecurityScheme
) security-scheme-description
-
Add a description to the Security Scheme. (default:
Authentication
) basic-security-scheme-value
-
Add a scheme value to the Basic HTTP Security Scheme. (default:
basic
) jwt-security-scheme-value
-
Add a scheme value to the JWT Security Scheme. (default:
bearer
) jwt-bearer-format
-
Add a bearer format to the JWT Security Scheme. (default:
JWT
) oidc-open-id-connect-url
-
Add a openIdConnectUrl value to the OIDC Security Scheme
oauth2-implicit-refresh-url
-
Add a implicit flow refreshUrl value to the OAuth2 Security Scheme
oauth2-implicit-authorization-url
-
Add an implicit flow authorizationUrl value to the OAuth2 Security Scheme
oauth2-implicit-token-url
-
Add an implicit flow tokenUrl value to the OAuth2 Security Scheme
open-api-version
-
Override the openapi version in the Schema document
info-title
-
Set the title in Info tag in the Schema document
info-version
-
Set the version in Info tag in the Schema document
info-description
-
Set the description in Info tag in the Schema document
info-terms-of-service
-
Set the terms of the service in Info tag in the Schema document
info-contact-email
-
Set the contact email in Info tag in the Schema document
info-contact-name
-
Set the contact name in Info tag in the Schema document
info-contact-url
-
Set the contact url in Info tag in the Schema document
info-license-name
-
Set the license name in Info tag in the Schema document
info-license-url
-
Set the license url in Info tag in the Schema document
operation-id-strategy
-
Set the strategy to automatically create an operation Id. Possible values:
method
,class-method
,package-class-method
Mail Sender
You can send emails by using Quarkus Mailer extension:
./mvnw quarkus:add-extension
-Dextensions="io.quarkus:quarkus-mailer"
You can inject two possible classes io.quarkus.mailer.Mailer
for synchronous API or io.quarkus.mailer.reactive.ReactiveMailer
for asynchronous/reactive API.
@Inject
Mailer mailer;
And then you can use them to send an email:
mailer.send(
Mail.withText("to@acme.org", "Subject", "Body")
);
Reactive Mail client
@Inject
ReactiveMailer reactiveMailer;
CompletionStage<Response> stage =
reactiveMailer.send(
Mail.withText("to@acme.org", "Subject", "Body")
)
.subscribeAsCompletionStage()
.thenApply(x -> Response.accepted().build());
If you are using quarkus-resteasy-mutiny , you can return io.smallrye.mutiny.Uni type.
|
Mail
class contains methods to add cc
, bcc
, headers
, bounce address
, reply to
, attachments
, inline attachments
and html body
.
mailer.send(Mail.withHtml("to@acme.org", "Subject", body)
.addInlineAttachment("quarkus.png",
new File("quarkus.png"),
"image/png", "<my-image@quarkus.io>"));
If you need deep control you can inject Vert.x mail client @Inject MailClient client;
|
You need to configure SMTP properties to be able to send an email:
quarkus.mailer.from=test@quarkus.io
quarkus.mailer.host=smtp.sendgrid.net
quarkus.mailer.port=465
quarkus.mailer.ssl=true
quarkus.mailer.username=....
quarkus.mailer.password=....
List of Mailer parameters.
quarkus.
as a prefix is skipped in the next table.
Parameter | Default | Description |
---|---|---|
|
Default address. |
|
|
false in |
Emails not sent, just printed and stored in a |
|
Default address. |
|
|
mandatory |
SMTP host. |
|
25 |
SMTP port. |
|
The username. |
|
|
The password. |
|
|
|
Enables SSL. |
|
false |
Trust all certificates. |
|
10 |
Max open connections . |
|
Hostname for |
|
|
|
Connection pool enabled. |
|
|
Disable ESMTP. |
|
|
TLS security mode. |
|
|
Login mode. |
|
All methods. |
Space-separated list. |
|
Path of the key store. |
|
|
Key store password. |
if you enable SSL for the mailer and you want to build a native executable, you will need to enable the SSL support quarkus.ssl.native=true .
|
Testing
If quarkus.mailer.mock
is set to true
, which is the default value in dev
and test
mode, you can inject MockMailbox
to get the sent messages.
@Inject
MockMailbox mailbox;
@BeforeEach
void init() {
mailbox.clear();
}
List<Mail> sent = mailbox
.getMessagesSentTo("to@acme.org");
Scheduled Tasks
You can schedule periodic tasks with Quarkus.
@ApplicationScoped
public class CounterBean {
@Scheduled(every="10s", delayed="1s")
void increment() {}
@Scheduled(cron="0 15 10 * * ?")
void morningTask() {}
}
every
and cron
parameters can be surrounded with {}
and the value is used as config property to get the value.
@Scheduled(cron = "${morning.check.cron.expr}") void morningTask() {} @Scheduled(cron = "${myMethod.cron.expr:0 0 15 ? * MON *}") void myMethod() {}
And configure the property into application.properties
:
morning.check.cron.expr=0 15 10 * * ?
morning.check.cron.expr=disabled
By default Quarkus expresion is used, but you can change that by setting quarkus.scheduler.cron-type
property.
quarkus.scheduler.cron-type=unix
org.quartz.Scheduler
can be injected as any other bean and scehdule jobs programmatically.
@Inject
org.quartz.Scheduler quartz;
quartz.scheduleJob(job, trigger);
Quarkus provides a built-in bean of type io.quarkus.scheduler.Scheduler
:
@Inject
io.quarkus.scheduler.Scheduler scheduler;
scheduler.pause(); // Pause all
scheduler.pause("myIdentity"); // Pause a specific scheduled method by its identity
scheduler.resume("myIdentity");
scheduler.resume()
Kogito
Quarkus integrates with Kogito, a next-generation business automation toolkit from Drools and jBPM projects for adding business automation capabilities.
To start using it you only need to add the next extension:
./mvnw quarkus:add-extension
-Dextensions="kogito"
DevServices
When testing or running in dev mode Quarkus can even provide you with a zero config Kogito out of the box.
Possible configuration values prefixed with quarkus.kogito
:
devservices.enabled
-
If devservices is enabled or not. (default:
true
) devservices.image-name
-
The container image name to use instead of the default one. (default:
quay.io/kiegroup/kogito-data-index-ephemeral
) devservices.port
-
Optional fixed port the dev service will listen to. (default:
8180
) devservices.shared
-
Indicates if the Data Index instance managed by Quarkus Dev Services is shared. When shared, Quarkus looks for running containers using label-based service discovery. (default:
true
) devservices.service-name
-
The value of the
kogito-dev-service-data-index
label attached to the started container. (default:kogito-data-index
)
Apache Tika
Quarkus integrates with Apache Tika to detect and extract metadata/text from different file types:
./mvnw quarkus:add-extension
-Dextensions="quarkus-tika"
@Inject
io.quarkus.tika.TikaParser parser;
@POST
@Path("/text")
@Consumes({ "text/plain", "application/pdf",
"application/vnd.oasis.opendocument.text" })
@Produces(MediaType.TEXT_PLAIN)
public String extractText(InputStream stream) {
return parser.parse(stream).getText();
}
You can configure Apache Tika in application.properties
file by using next properties prefixed with quarkus
:
Parameter | Default | Description |
---|---|---|
|
|
Path to the Tika configuration resource. |
|
CSV of the abbreviated or full parser class to be loaded by the extension. |
|
|
|
The document may have other embedded documents. Set if autmatically append. |
JGit
Quarkus integrates with JGit to integrate with Git repositories:
./mvnw quarkus:add-extension
-Dextensions="quarkus-jgit"
And then you can start using JGit:
try (Git git = Git.cloneRepository()
.setDirectory(tmpDir)
.setURI(url)
.call()) {
return tmpDir.toString();
}
When running in native mode, make sure to configure SSL access correctly quarkus.ssl.native=true (Native and SSL).
|
Web Resources
You can serve web resources with Quarkus.
You need to place web resources at src/main/resources/META-INF/resources
and then they are accessible (ie http://localhost:8080/index.html)
By default static resources as served under the root context.
You can change this by using quarkus.http.root-path
property.
Transactional Memory
Quarkus integrates with the Software Transactional Memory (STM) implementation provided by the Narayana project.
./mvnw quarkus:add-extension
-Dextensions="narayana-stm"
Transactional objects must be interfaces and annotated with org.jboss.stm.annotations.Transactional
.
@Transactional
@NestedTopLevel
public interface FlightService {
int getNumberOfBookings();
void makeBooking(String details);
}
The pessimistic strategy is the default one, you can change to optimistic by using @Optimistic
.
Then you need to create the object inside org.jboss.stm.Container
.
Container<FlightService> container = new Container<>();
FlightServiceImpl instance = new FlightServiceImpl();
FlightService flightServiceProxy = container.create(instance);
The implementation of the service sets the locking and what needs to be saved/restored:
import org.jboss.stm.annotations.ReadLock;
import org.jboss.stm.annotations.State;
import org.jboss.stm.annotations.WriteLock;
public class FlightServiceImpl
implements FlightService {
@State
private int numberOfBookings;
@ReadLock
public int getNumberOfBookings() {
return numberOfBookings;
}
@WriteLock
public void makeBooking(String details) {
numberOfBookings += 1;
}
}
Any member is saved/restored automatically (@State
is not mandatory).
You can use @NotState
to avoid behaviour.
Transaction boundaries
Declarative
-
@NestedTopLevel
: Defines that the container will create a new top-level transaction for each method invocation. -
@Nested
: Defines that the container will create a new top-level or nested transaction for each method invocation.
Programmatically
AtomicAction aa = new AtomicAction();
aa.begin();
{
try {
flightService.makeBooking("BA123 ...");
taxiService.makeBooking("East Coast Taxis ...");
aa.commit();
} catch (Exception e) {
aa.abort();
}
}
Quartz
Quarkus integrates with Quartz to schedule periodic clustered tasks.
./mvnw quarkus:add-extension
-Dextensions="quartz"
@ApplicationScoped
public class TaskBean {
@Transactional
@Scheduled(every = "10s")
void schedule() {
Task task = new Task();
task.persist();
}
}
To configure in clustered mode vida DataSource:
quarkus.datasource.url=jdbc:postgresql://localhost/quarkus_test
quarkus.datasource.driver=org.postgresql.Driver
# ...
quarkus.quartz.clustered=true
quarkus.quartz.store-type=db
You need to define the datasource used by clustered mode and also import the database tables following the Quartz schema. |
Quartz can be configured usinf the following properties with quarkus.quartz
prefix:
clustered
-
Enable cluster mode or not.
store-type
-
The type of store to use. Possible values:
ram
,db
(default:ram
) datasource
-
The name of the datasource to use.
table-prefix
-
The prefix for quartz job store tables. (default:
QRTZ_
) trigger-listeners.<name>.class
-
Class name for the trigger.
trigger-listeners.<name>.property-name
-
The properties passed to the class.
job-listeners.<name>.class
-
Class name for the job.
job-listeners.<name>.property-name
-
The properties passed to the class.
plugins.<name>.class
-
Class name for the plugin.
plugins.<name>.property-name
-
The properties passed to the class.
instance-name
-
The name of the Quartz instance. (default:
QuarkusQuartzScheduler
) thread-count
-
The size of scheduler thread pool. (default:
25
) thread-priority
-
Thread priority of worker threads in the pool. (default:
5
) force-start
-
The scheduler will be started even if no scheduled business methods are found.
start-mode
-
Scheduler can be started in different modes:
normal
,forced
orhalted
. (default:normal
)
Qute
Qute is a templating engine designed specifically to meet the Quarkus needs.
Templates should be placed by default at src/main/resources/templates
aand subdirectories.
./mvnw quarkus:add-extension
-Dextensions="quarkus-resteasy-qute"
Templates can be defined in any format, in case of HTML:
{@org.acme.Item item}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{item.name}</title>
</head>
<body>
<h1>{item.name ?: 'Unknown'}</h1>
<h2>{str:reverse('Hello')}</h2>
<div>Price: {item.price}</div>
{#if item.price > 100}
<div>Discounted Price: {item.discountedPrice}</div>
{/if}
</body>
</html>
First line is not mandatory but helps on doing property checks at compilation time.
Including templates passing parameters:
<html>
<head>
<meta charset="UTF-8">
<title>Simple Include</title>
</head>
<body>
{#include foo limit=10 /}
</body>
</html>
To render the template:
public class Item {
public String name;
public BigDecimal price;
}
@Inject
io.quarkus.qute.Template item;
@GET
@Path("{id}")
@Produces(MediaType.TEXT_HTML)
public TemplateInstance get(@PathParam("id") Integer id) {
return item.data("item", service.findItem(id));
}
@TemplateExtension
static BigDecimal discountedPrice(Item item) {
return item.price.multiply(new BigDecimal("0.9"));
}
@TemplateExtension(namespace = "str")
public static class StringExtensions {
static String reverse(String val) {
return new StringBuilder(val).reverse().toString();
}
}
If @ResourcePath
is not used in Template
then the name of the field is used as file name.
In this case the file should be src/main/resources/templates/item.{}
.
Extension of the file is not required to be set.
discountedPrice
is not a field of the POJO but a method call.
Method definition must be annotated with @TemplateExtension
and be static method.
First parameter is used to match the base object (Item
).
@TemplateExtension
can be used at class level:
@TemplateExtension
public class MyExtensions {
static BigDecimal discountedPrice(Item item) {
return item.price.multiply(new BigDecimal("0.9"));
}
}
Methods with multiple parameters are supported too:
{item.discountedPrice(2)}
static BigDecimal discountedPrice(Item item, int scale) {
return item.price.multiply(scale);
}
Qute for
syntax supports any instance of Iterable
, Map.EntrySet
, Stream
or Integer
.
{#for i in total}
{i}:
{/for}
The next map methods are supported:
{#for key in map.keySet}
{#for value in map.values}
{map.size}
{#if map.isEmpty}
{map['foo']
The next list methods are supported:
{list[0]}
The next number methods are supported:
{#if counter.mod(5) == 0}
Switch/When
{#switch person.name}
{#case 'John'}
Hey John!
{#case 'Mary'}
Hey Mary!
{/switch}
{#when items.size}
{#is 1} (1)
There is exactly one item!
{#is > 10} (2)
There are more than 10 items!
{#else} (3)
There are 2 -10 items!
{/when}
Following operators can be used either in when
and switch
: not, ne, !=
, gt, >
, ge, >=
, lt, <
, le, <=
, in
, ni, !in
.
eval
{#eval myData.template name='Mia' /}
The result of myData.template
will be used as the template.
Message Bundling
@io.quarkus.qute.i18n.MessageBundle
public interface AppMessages {
@io.quarkus.qute.i18n.Message("Hello {name}!")
String hello_name(String name);
}
There are 3 methods to inject the message:
MessageBundles.get(AppMessages.class).hello_name("Lucie");
or
@Inject AppMessages app;
app.hello_name("Lucie");
or
<p>{msg:hello_name('Lucie')}</p>
Localization
There are two ways to set localized message:
@io.quarkus.qute.i18n.Localized("de")
public interface GermanAppMessages {
@Override
@io.quarkus.qute.i18n.Message("Hallo {name}!")
String hello_name(String name);
}
or
hello_name=Hallo {name}!
You can render programmaticaly the templates too:
// file located at src/main/resources/templates/reports/v1/report_01.{}
@ResourcePath("reports/v1/report_01")
Template report;
String output = report
.data("samples", service.get())
.render();
Value Resolvers
Value resolvers are invoked when evaluating expressions.
void configureEngine(@Observes EngineBuilder builder) {
builder.addValueResolver(ValueResolver.builder()
.appliesTo(ctx -> ctx.getBase() instanceof Long && ctx.getName().equals("tenTimes"))
.resolveSync(ctx -> (Long) ctx.getBase() * 10)
.build());
}
Content Filters
Content filters can be used to modify the template contents before parsing.
void configureEngine(@Observes EngineBuilder builder) {
builder.addParserHook(new ParserHook() {
@Override
public void beforeParsing(ParserHelper parserHelper) {
parserHelper.addContentFilter(contents -> contents.replace("${", "$\\{"));
}
});
}
Reactive and Async
CompletionStage<String> async = report.renderAsync();
Multi<String> publisher = report.createMulti();
Uni<String> content = io.smallrye.mutiny.Uni.createFrom()
.completionStage(() -> report.renderAsync());
Qute Mail Integration
@Inject
MailTemplate hello;
CompletionStage<Void> c = hello.to("to@acme.org")
.subject("Hello from Qute template")
.data("name", "John")
.send();
INFO: Template located at src/main/resources/templates/hello.[html|txt]
.
Sentry
Quarkus integrates with Sentry for logging errors into an error monitoring system.
./mvnw quarkus:add-extension
-Dextensions="quarkus-logging-sentry"
And the configuration to send all errors occuring in the package org.example
to Sentrty with DSN https://abcd@sentry.io/1234
:
quarkus.log.sentry=true
quarkus.log.sentry.dsn=https://abcd@sentry.io/1234
quarkus.log.sentry.level=ERROR
quarkus.log.sentry.in-app-packages=org.example
Full list of configuration properties having quarkus.log
as prefix:
sentry.enable
-
Enable the Sentry logging extension (default: false)
sentry.dsn
-
Where to send events.
sentry.level
-
Log level (default:
WARN
) sentry.server-name
-
Sets the server name that will be sent with each event.
sentry.in-app-packages
-
Configure which package prefixes your application uses.
JSch
Quarkus integrates with Jsch for SSH communication.
./mvnw quarkus:add-extension
-Dextensions="quarkus-jsch"
JSch jsch = new JSch();
Session session = jsch.getSession(null, host, port);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
Cache
Quarkus can cache method calls by using as key the tuple (method + arguments).
./mvnw quarkus:add-extension
-Dextensions="cache"
@io.quarkus.cache.CacheResult(cacheName = "weather-cache")
public String getDailyForecast(LocalDate date, String city) {}
@CacheInvalidate
removes the element represented by the calculated cache key from cache.
@CacheInvalidateAll
removes all entries from the cache.
@CacheKey
to specifically set the arguments to be used as key instead of all.
@ApplicationScoped
public class CachedBean {
@CacheResult(cacheName = "foo")
public Object load(Object key) {}
@CacheInvalidate(cacheName = "foo")
public void invalidate(Object key) {}
@CacheInvalidateAll(cacheName = "foo")
public void invalidateAll() {}
}
Programmatic API
@Inject
CacheManager cacheManager;
@Inject
@CacheName("my-cache")
Cache cache;
public Uni<String> getNonBlockingExpensiveValue(Object key) {
return cache.get(key, k -> {
});
}
public String getBlockingExpensiveValue(Object key) {
return cache.get(key, k -> {
}).await().indefinitely(); (4)
}
You can disable the caching system by setting quarkus.cache.enabled
property to false
.
This extension uses Caffeine as its underlying caching provider.
Each cache can be configured individually:
quarkus.cache.caffeine."foo".initial-capacity=10
quarkus.cache.caffeine."foo".maximum-size=20
quarkus.cache.caffeine."foo".expire-after-write
quarkus.cache.caffeine."bar".maximum-size=1000
Full list of configuration properties having quarkus.cache.caffeine.[cache-name]
as prefix:
initial-capacity
-
Minimum total size for the internal data structures.
maximum-size
-
Maximum number of entries the cache may contain.
expire-after-write
-
Specifies that each entry should be automatically removed from the cache once a fixed duration has elapsed after the entry’s creation, or last write.
expire-after-access
-
Specifies that each entry should be automatically removed from the cache once a fixed duration has elapsed after the entry’s creation, or last write.
You can multiple cache annotations on a single method. |
If you see a javax.enterprise.context.ContextNotActiveException
, you need to add the quarkus-smallrye-context-propagation
extension.
Banner
Banner is printed by default. It is not an extension but placed in the core.
quarkus.banner.path
-
Path is relative to root of the classpath. (default:
default_banner.txt
) quarkus.banner.enabled
-
Enables banner. (default :
true
)
OptaPlanner
Quarkus integrates with OptaPlanner.
./mvnw quarkus:add-extension
-Dextensions="quarkus-optaplanner, quarkus-optaplanner-jackson"
@PlanningSolution
public class TimeTable {
}
@Inject
private SolverManager<TimeTable, UUID> solverManager;
UUID problemId = UUID.randomUUID();
SolverJob<TimeTable, UUID> solverJob = solverManager.solve(problemId, problem);
TimeTable solution = solverJob.getFinalBestSolution();
Possible configuration options prefixed with quarkus.optaplanner
:
solver-config-xml
-
A classpath resource to read the solver configuration XML. Not mandatory.
solver.environment-mode
-
Enable runtime assertions to detect common bugs in your implementation during development. Possible values:
FAST_ASSERT
,FULL_ASSERT
,NON_INTRUSIVE_FULL_ASSERT
,NON_REPRODUCIBLE
,REPRODUCIBLE
. (default:REPRODUCIBLE
) solver.move-thread-count
-
Enable multithreaded solving for a single problem. Possible values:
MOVE_THREAD_COUNT_NONE
,MOVE_THREAD_COUNT_AUTO
, a number or formula based on the available processors. (default:MOVE_THREAD_COUNT_NONE
) solver.termination.spent-limit
-
How long the solver can run. (ie
5s
) solver.termination.unimproved-spent-limit
-
How long the solver can run without finding a new best solution after finding a new best solution. (ie
2h
) solver.termination.best-score-limit
-
Terminates the solver when a specific or higher score has been reached. (ie
0hard/-1000soft
) solver-manager.parallel-solver-count
-
The number of solvers that run in parallel. (default:
PARALLEL_SOLVER_COUNT_AUTO
)
Context Propagation
./mvnw quarkus:add-extension
-Dextensions="quarkus-smallrye-context-propagation"
If using mutiny
extension together you already get context propagation for ArC, RESTEasy and transactions.
With CompletionStage
you need to:
@Inject ThreadContext threadContext;
@Inject ManagedExecutor managedExecutor;
threadContext.withContextCapture(..)
.thenApplyAsync(r -> {}, managedExecutor);
If you are going to use security in a reactive environment you will likely need Smallrye Content Propagation to propagate the identity throughout the reactive callback.
Configuration from HasiCorp Consul
You can read runtime configuration from HashiCorp Consul.
./mvnw quarkus:add-extension
-Dextensions="consul-config"
You need to configure Consul:
quarkus.consul-config.enabled=true
quarkus.consul-config.agent.host-port=localhost:8500
quarkus.consul-config.properties-value-keys=config/consul-test
@ConfigProperty(name = "greeting.message")
String message;
In Consul:
"Key": "config/consul-test",
"Value": "greeting.message=Hello from Consul"
Possible configuration parameters, prefixed with quarkus.consul-config
:
enabled
-
The application will attempt to look up the configuration from Consul. (default:
false
) prefix
-
Common prefix that all keys share when looking up the keys from Consul. The prefix is not included in the keys of the user configuration
- raw-value-keys
-
Keys whose value is a raw string. The keys that end up in the user configuration are the keys specified her with '/' replaced by '.'
properties-value-keys
-
Keys whose value represents a properties-like file conttent.
fail-on-missing-key
-
If the application will not start if any of the configured config sources cannot be located. (default:
true
)
trust-store
-
TrustStore to be used containing the SSL certificate used by Consul agent.
trust-store-password
-
Password of TrustStore to be used containing the SSL certificate used by Consul agent.
key-password
-
Password to recover key from KeyStore for SSL client authentication with Consul agent.
agent.host-port
-
Consul agent host. (default:
localhost:8500
) agent.use-https
-
Use HTTPS when communicating with the agent. (default:
false
) agent.token
-
Consul token to be provided when authentication is enabled.
agent.key-store
-
KeyStore (classpath or filesystem) to be used containing the SSL certificate used by Consul agent.
agent.key-store-password
-
Password of KeyStore.
agent.trust-certs
-
To trust all certificates or not.
agent.connection-timeout
-
Connection timeout. (default:
10S
) agent.read-timeout
-
Reading timeout. (default:
60S
)
Amazon Alexa
You can use Amazon Alexa by adding the extension:
./mvnw quarkus:add-extension
-Dextensions="quarkus-amazon-alexa"
WebJar Locator
To change how you can refer to webjars skipping the version part you can use WebJars locator extension.
./mvnw quarkus:add-extension
-Dextensions="webjars-locator"
Then the JavaScript location is changed from /webjars/jquery/3.1.1/jquery.min.js
to /webjars/jquery/jquery.min.js
in your HTML files.
Amazon SES
mvn quarkus:add-extension
-Dextensions="amazon-ses"
@Inject
software.amazon.awssdk.services.ses.SesClient sesClient;
@Inject
software.amazon.awssdk.services.ses.SesAsyncClient sesClient;
quarkus.ses.endpoint-override=http://localhost:8012
quarkus.ses.aws.region=us-east-1
quarkus.ses.aws.credentials.type=static
quarkus.ses.aws.credentials.static-provider.access-key-id=test-key
quarkus.ses.aws.credentials.static-provider.secret-access-key=test-secret
You need to set a HTTP client either URL Connection
:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>url-connection-client</artifactId>
</dependency>
or Apache HTTP:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>apache-client</artifactId>
</dependency>
quarkus.ses.sync-client.type=apache
Or async:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>netty-nio-client</artifactId>
</dependency>
Configuration properties are the same as Amazon DynamoDB but changing the prefix from dynamodb
to ses
.
Jbang
Creating an initial script:
jbang scripting/quarkusapp.java
Adding Quarkus dependencies in script:
//DEPS io.quarkus:quarkus-resteasy:{quarkus-version}
Put some Quarkus CLI code:
@Path("/hello")
@ApplicationScoped
public class quarkusapp {
@GET
public String sayHello() {
return "hello";
}
public static void main(String[] args) {
Quarkus.run(args);
}
}
To run the script:
jbang quarkusapp.java
A Maven goal is provided to scaffold a project:
mvn io.quarkus:quarkus-maven-plugin:<version>:create-jbang
Narayana LRA
The LRA (short for Long Running Action) participant extension is useful in microservice based designs where different services can benefit from a relaxed notion of distributed consistency.
The extension integrates with MicroProfile LRA
./mvnw quarkus:add-extension
-Dextensions="narayana-lra"
The only LRA specific property is quarkus.lra.coordinator-url=<url>
which specifies the HTTP endpoint of an external coordinator,
@Path("/")
@ApplicationScoped
public class SimpleLRAParticipant
{
@LRA(LRA.Type.REQUIRES_NEW)
@Path("/work")
@PUT
public void doInNewLongRunningAction(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId)
{
}
@org.eclipse.microprofile.lra.annotation.Complete
@Path("/complete")
@PUT
public Response completeWork(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId,
String userData)
{
return Response.ok(ParticipantStatus.Completed.name()).build();
}
@org.eclipse.microprofile.lra.annotation.Compensate
@Path("/compensate")
@PUT
public Response compensateWork(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId,
String userData)
{
return Response.ok(ParticipantStatus.Compensated.name()).build();
}
}
@LRA
can be used in differrent methods, so it’s started in one method and finished in another resource using end
attribute:
@LRA(value = LRA.Type.REQUIRED, // if there is no incoming context a new one is created
cancelOn = {
Response.Status.INTERNAL_SERVER_ERROR // cancel on a 500 code
},
cancelOnFamily = {
Response.Status.Family.CLIENT_ERROR // cancel on any 4xx code
},
end = false) // the LRA will continue to run when the method finishes
@LRA(LRA.Type.MANDATORY, // requires an active context before method can be executed
end = true)
Spring DI
Quarkus provides a compatibility layer for Spring dependency injection.
./mvnw quarkus:add-extension
-Dextensions="quarkus-spring-di"
Some examples of what you can do. Notice that annotations are the Spring original ones.
@Configuration public class AppConfiguration { @Bean(name = "capitalizeFunction") public StringFunction capitalizer() { return String::toUpperCase; } }
Or as a component:
@Component("noopFunction")
public class NoOpSingleStringFunction
implements StringFunction {
}
Also as a service and injection properties from application.properties
.
@Service
public class MessageProducer {
@Value("${greeting.message}")
String message;
}
And you can inject using Autowired
or constructor in a component and in a JAX-RS resource too.
@Component
public class GreeterBean {
private final MessageProducer messageProducer;
@Autowired @Qualifier("noopFunction")
StringFunction noopStringFunction;
public GreeterBean(MessageProducer messageProducer) {
this.messageProducer = messageProducer;
}
}
Spring Boot Configuration
Quarkus provides a compatibility layer for Spring Boot ConfigurationProperties
.
./mvnw quarkus:add-extension
-Dextensions="quarkus-spring-boot-properties"
@ConfigurationProperties("example")
public final class ClassProperties {
private String value;
private AnotherClass anotherClass;
// getters/setters
}
example.value=class-value
example.anotherClass.value=true
Spring Cloud Config Client
Quarkus integrates Spring Cloud Config Client and MicroProfile Config spec.
./mvnw quarkus:add-extension
-Dextensions="quarkus-spring-cloud-config-client"
You need to configure the extension:
quarkus.spring-cloud-config.uri=http://localhost:8089
quarkus.spring-cloud-config.username=user
quarkus.spring-cloud-config.password=pass
quarkus.spring-cloud-config.enabled=true
@ConfigProperty(name = "greeting.message")
String greeting;
Prefix is quarkus.spring-cloud-config
.
uri
-
Base URI where the Spring Cloud Config Server is available. (default:
localhost:8888
) username
-
Username to be used if the Config Server has BASIC Auth enabled.
password
-
Password to be used if the Config Server has BASIC Auth enabled.
enabled
-
Enables read configuration from Spring Cloud Config Server. (default:
false
) fail-fast
-
True to not start application if cannot access to the server. (default:
false
) connection-timeout
-
The amount of time to wait when initially establishing a connection before giving up and timing out. (default:
10S
) read-timeout
-
The amount of time to wait for a read on a socket before an exception is thrown. (default:
60S
) label
-
The label to be used to pull remote configuration properties.
Spring Web
Quarkus provides a compatibility layer for Spring Web.
./mvnw quarkus:add-extension
-Dextensions="quarkus-spring-web"
Specifically supports the REST related features.
Notice that infrastructure things like BeanPostProcessor
will not be executed.
@RestController
@RequestMapping("/greeting")
public class GreetingController {
private final GreetingBean greetingBean;
public GreetingController(GreetingBean greetingBean) {
this.greetingBean = greetingBean;
}
@GetMapping("/{name}")
public Greeting hello(@PathVariable(name = "name")
String name) {
return new Greeting(greetingBean.greet(name));
}
}
Supported annotations are: RestController
, RequestMapping
, GetMapping
, PostMapping
, PutMapping
, DeleteMapping
, PatchMapping
, RequestParam
, RequestHeader
, MatrixVariable
, PathVariable
, CookieValue
, RequestBody
, ResponseStatus
, ExceptionHandler
and RestControllerAdvice
.
If you scaffold the project with spring-web extension, then Spring Web annotations are sed in the generated project. mvn io.quarkus:quarkus-maven-plugin:2.7.0.Final:create … -Dextensions="spring-web" .
|
The next return types are supported: org.springframework.http.ResponseEntity
and java.util.Map
.
The next parameter types are supported: An Exception
argument and ServletRequest
/HttpServletRequest
(adding quarkus-undertow
dependency).
Spring Data JPA
While users are encouraged to use Hibernate ORM with Panache for Relational Database access, Quarkus provides a compatibility layer for Spring Data JPA repositories.
./mvnw quarkus:add-extension
-Dextensions="quarkus-spring-data-jpa"
INFO: Of course you still need to add the JDBC driver, and configure it in application.properties
.
public interface FruitRepository
extends CrudRepository<Fruit, Long> {
List<Fruit> findByColor(String color);
}
And then you can inject it either as shown in Spring DI or in Spring Web.
Interfaces supported:
-
org.springframework.data.repository.Repository
-
org.springframework.data.repository.CrudRepository
-
org.springframework.data.repository.PagingAndSortingRepository
-
org.springframework.data.jpa.repository.JpaRepository
.
INFO: Generated repositories are automatically annotated with @Transactional
.
Repository fragments is also supported:
public interface PersonRepository
extends JpaRepository<Person, Long>, PersonFragment {
void makeNameUpperCase(Person person);
}
User defined queries:
@Query("select m from Movie m where m.rating = ?1")
Iterator<Movie> findByRating(String rating);
@Modifying
@Query("delete from Movie where rating = :rating")
void deleteByRating(@Param("rating") String rating);
@Query(value = "SELECT COUNT(*), publicationYear FROM Book GROUP BY publicationYear")
List<BookCountByYear> findAllByPublicationYear2();
interface BookCountByYear {
int getPublicationYear();
Long getCount();
}
What is currently unsupported:
-
Methods of
org.springframework.data.repository.query.QueryByExampleExecutor
-
QueryDSL support
-
Customizing the base repository
-
java.util.concurrent.Future
as return type -
Native and named queries when using
@Query
Spring Data Rest
While users are encouraged to use REST Data with Panache for the REST data access endpoints generation, Quarkus provides a compatibility layer for Spring Data REST in the form of the spring-data-rest
extension.
./mvnw quarkus:add-extension
-Dextensions="spring-data-rest"
import java.util.Optional;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.core.annotation.RestResource;
@RepositoryRestResource(exported = false, path = "/my-fruits")
public interface FruitsRepository extends CrudRepository<Fruit, Long> {
@RestResource(exported = true)
Optional<Fruit> findById(Long id);
@RestResource(exported = true)
Iterable<Fruit> findAll();
}
The spring-data-jpa
extension will generate an implementation for this repository. Then the spring-data-rest
extension will generate a REST CRUD resource for it.
The following interfaces are supported:
-
org.springframework.data.repository.CrudRepository
-
org.springframework.data.repository.PagingAndSortingRepository
-
org.springframework.data.jpa.repository.JpaRepository
Spring Security
Quarkus provides a compatibility layer for Spring Security.
./mvnw quarkus:add-extension
-Dextensions="spring-security"
You need to choose a security extension to define user, roles, … such as openid-connect
, oauth2
, properties-file
or security-jdbc
as seen at RBAC.
Then you can use Spring Security annotations to protect the methods:
@Secured("admin")
@GetMapping
public String hello() {
return "hello";
}
Quarkus provides support for some of the most used features of Spring Security’s @PreAuthorize
annotation.
Some examples:
hasRole
-
@PreAuthorize("hasRole('admin')")
-
@PreAuthorize("hasRole(@roles.USER)")
whereroles
is a bean defined with@Component
annotation andUSER
is a public field of the class.
hasAnyRole
-
@PreAuthorize("hasAnyRole(@roles.USER, 'view')")
Permit and Deny All
-
@PreAuthorize("permitAll()")
-
@PreAuthorize("denyAll()")
Anonymous and Authenticated
-
@PreAuthorize("isAnonymous()")
-
@PreAuthorize("isAuthenticated()")
Expressions
-
Checks if the current logged in user is the same as the username method parameter:
@PreAuthorize("#person.name == authentication.principal.username")
public void doSomethingElse(Person person){}
-
Checks if calling a method if user can access:
@PreAuthorize("@personChecker.check(#person, authentication.principal.username)")
public void doSomething(Person person){}
@Component
public class PersonChecker {
public boolean check(Person person, String username) {
return person.getName().equals(username);
}
}
-
Combining expressions:
@PreAuthorize("hasAnyRole('user', 'admin') AND #user == principal.username")
public void allowedForUser(String user) {}
Spring Cache
Quarkus provides a compatibility layer for Spring dependency injection.
./mvnw quarkus:add-extension
-Dextensions="spring-cache"
@org.springframework.cache.annotation.Cacheable("someCache")
public Greeting greet(String name) {}
Quarkus provides compatibility with the following Spring Cache annotations:
-
@Cacheable
-
@CachePut
-
@CacheEvict
Spring Schedule
Quarkus provides a compatibility layer for Spring Scheduled annotation.
./mvnw quarkus:add-extension
-Dextensions="spring-scheduled"
@org.springframework.scheduling.annotation.Scheduled(cron="*/5 * * * * ?")
void cronJob() {
System.out.println("Cron expression hardcoded");
}
@Scheduled(fixedRate = 1000)
@Scheduled(cron = "{cron.expr}")