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=<IP_ADDRESS>
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=<IP_ADDRESS>: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 =<EMAIL_ADDRESS>})
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}