Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: quarkusio/quarkus
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 927b525352ebea5d6f5c01e808f013b08b646a4d
Choose a base ref
..
head repository: quarkusio/quarkus
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 1e0d016103b3768f090a419d99ed66f1b287c42c
Choose a head ref
5 changes: 5 additions & 0 deletions docs/src/main/asciidoc/appcds.adoc
Original file line number Diff line number Diff line change
@@ -87,6 +87,11 @@ This results in an archive generation process that on one hand is completely saf
As a result, users are expected to get a slightly more effective archive if they manually go through the hoops of generating the AppCDS archive.
====

[IMPORTANT]
====
AppCDS has improved significantly in the latest JDK releases. This means that to ensure the best possible improvements from it, make sure your projects targets the highest possible Java version (ideally 17 or 21).
====

=== Usage in containers

When building container images using the `quarkus-container-image-jib` extension, Quarkus automatically takes care of all the steps needed to generate the archive
Original file line number Diff line number Diff line change
@@ -6,61 +6,61 @@
import java.util.Optional;

import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;
import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithDefault;

@ConfigMapping(prefix = "quarkus.elasticsearch")
@ConfigRoot(phase = ConfigPhase.RUN_TIME)
public class ElasticsearchConfig {
public interface ElasticsearchConfig {

/**
* The list of hosts of the Elasticsearch servers.
*/
@ConfigItem(defaultValue = "localhost:9200")
public List<InetSocketAddress> hosts;
@WithDefault("localhost:9200")
List<InetSocketAddress> hosts();

/**
* The protocol to use when contacting Elasticsearch servers.
* Set to "https" to enable SSL/TLS.
*/
@ConfigItem(defaultValue = "http")
public String protocol;
@WithDefault("http")
String protocol();

/**
* The username for basic HTTP authentication.
*/
@ConfigItem
public Optional<String> username;
Optional<String> username();

/**
* The password for basic HTTP authentication.
*/
@ConfigItem
public Optional<String> password;
Optional<String> password();

/**
* The connection timeout.
*/
@ConfigItem(defaultValue = "1S")
public Duration connectionTimeout;
@WithDefault("1S")
Duration connectionTimeout();

/**
* The socket timeout.
*/
@ConfigItem(defaultValue = "30S")
public Duration socketTimeout;
@WithDefault("30S")
Duration socketTimeout();

/**
* The maximum number of connections to all the Elasticsearch servers.
*/
@ConfigItem(defaultValue = "20")
public int maxConnections;
@WithDefault("20")
int maxConnections();

/**
* The maximum number of connections per Elasticsearch server.
*/
@ConfigItem(defaultValue = "10")
public int maxConnectionsPerRoute;
@WithDefault("10")
int maxConnectionsPerRoute();

/**
* The number of IO thread.
@@ -72,28 +72,26 @@ public class ElasticsearchConfig {
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/_number_of_threads.html">
* number of threads</a>
*/
@ConfigItem
public Optional<Integer> ioThreadCounts;
Optional<Integer> ioThreadCounts();

/**
* Configuration for the automatic discovery of new Elasticsearch nodes.
*/
@ConfigItem
public DiscoveryConfig discovery;
DiscoveryConfig discovery();

@ConfigGroup
public static class DiscoveryConfig {
interface DiscoveryConfig {

/**
* Defines if automatic discovery is enabled.
*/
@ConfigItem(defaultValue = "false")
public boolean enabled;
@WithDefault("false")
boolean enabled();

/**
* Refresh interval of the node list.
*/
@ConfigItem(defaultValue = "5M")
public Duration refreshInterval;
@WithDefault("5M")
Duration refreshInterval();
}
}
Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@ public RestClient restClient() {
RestClientBuilder builder = RestClientBuilderHelper.createRestClientBuilder(config);

this.client = builder.build();
if (config.discovery.enabled) {
if (config.discovery().enabled()) {
this.sniffer = RestClientBuilderHelper.createSniffer(client, config);
}

Original file line number Diff line number Diff line change
@@ -34,9 +34,9 @@ private RestClientBuilderHelper() {
}

public static RestClientBuilder createRestClientBuilder(ElasticsearchConfig config) {
List<HttpHost> hosts = new ArrayList<>(config.hosts.size());
for (InetSocketAddress host : config.hosts) {
hosts.add(new HttpHost(host.getHostString(), host.getPort(), config.protocol));
List<HttpHost> hosts = new ArrayList<>(config.hosts().size());
for (InetSocketAddress host : config.hosts()) {
hosts.add(new HttpHost(host.getHostString(), host.getPort(), config.protocol()));
}

RestClientBuilder builder = RestClient.builder(hosts.toArray(new HttpHost[0]));
@@ -45,37 +45,37 @@ public static RestClientBuilder createRestClientBuilder(ElasticsearchConfig conf
@Override
public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) {
return requestConfigBuilder
.setConnectTimeout((int) config.connectionTimeout.toMillis())
.setSocketTimeout((int) config.socketTimeout.toMillis())
.setConnectTimeout((int) config.connectionTimeout().toMillis())
.setSocketTimeout((int) config.socketTimeout().toMillis())
.setConnectionRequestTimeout(0); // Avoid requests being flagged as timed out even when they didn't time out.
}
});

builder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
@Override
public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
if (config.username.isPresent()) {
if (!"https".equalsIgnoreCase(config.protocol)) {
if (config.username().isPresent()) {
if (!"https".equalsIgnoreCase(config.protocol())) {
LOG.warn("Using Basic authentication in HTTP implies sending plain text passwords over the wire, " +
"use the HTTPS protocol instead.");
}
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(config.username.get(), config.password.orElse(null)));
new UsernamePasswordCredentials(config.username().get(), config.password().orElse(null)));
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
}

if (config.ioThreadCounts.isPresent()) {
if (config.ioThreadCounts().isPresent()) {
IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
.setIoThreadCount(config.ioThreadCounts.get())
.setIoThreadCount(config.ioThreadCounts().get())
.build();
httpClientBuilder.setDefaultIOReactorConfig(ioReactorConfig);
}

httpClientBuilder.setMaxConnTotal(config.maxConnections);
httpClientBuilder.setMaxConnPerRoute(config.maxConnectionsPerRoute);
httpClientBuilder.setMaxConnTotal(config.maxConnections());
httpClientBuilder.setMaxConnPerRoute(config.maxConnectionsPerRoute());

if ("http".equalsIgnoreCase(config.protocol)) {
if ("http".equalsIgnoreCase(config.protocol())) {
// In this case disable the SSL capability as it might have an impact on
// bootstrap time, for example consuming entropy for no reason
httpClientBuilder.setSSLStrategy(NoopIOSessionStrategy.INSTANCE);
@@ -99,10 +99,10 @@ public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpCli

public static Sniffer createSniffer(RestClient client, ElasticsearchConfig config) {
SnifferBuilder builder = Sniffer.builder(client)
.setSniffIntervalMillis((int) config.discovery.refreshInterval.toMillis());
.setSniffIntervalMillis((int) config.discovery().refreshInterval().toMillis());

// https discovery support
if ("https".equalsIgnoreCase(config.protocol)) {
if ("https".equalsIgnoreCase(config.protocol())) {
NodesSniffer hostsSniffer = new ElasticsearchNodesSniffer(
client,
ElasticsearchNodesSniffer.DEFAULT_SNIFF_REQUEST_TIMEOUT, // 1sec
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package io.quarkus.resteasy.reactive.jackson.deployment.test;

import static io.restassured.RestAssured.given;
import static io.restassured.RestAssured.when;
import static org.hamcrest.CoreMatchers.equalTo;

import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.MediaType;
@@ -18,6 +21,7 @@

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import io.quarkus.arc.Unremovable;
import io.quarkus.test.QuarkusUnitTest;
@@ -32,26 +36,99 @@ public class CustomObjectMapperTest {
* `objectMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);`
*/
@Test
void serverShouldUnwrapRootElement() {
given().body("{\"Request\":{\"value\":\"good\"}}")
void test() {
given().body("{\"Request\":{\"value\":\"FIRST\"}}")
.contentType(ContentType.JSON)
.post("/server")
.post("/server/dummy")
.then()
.statusCode(HttpStatus.SC_OK)
.body(equalTo("good"));
.body(equalTo("0"));

// ContextResolver was invoked for both reader and writer
when().get("/server/count")
.then()
.statusCode(HttpStatus.SC_OK)
.body(equalTo("2"));

given().body("{\"Request2\":{\"value\":\"FIRST\"}}")
.contentType(ContentType.JSON)
.post("/server/dummy2")
.then()
.statusCode(HttpStatus.SC_OK)
.body(equalTo("0"));

// ContextResolver was invoked for both reader and writer because different types where used
when().get("/server/count")
.then()
.statusCode(HttpStatus.SC_OK)
.body(equalTo("4"));

given().body("{\"Request\":{\"value\":\"FIRST\"}}")
.contentType(ContentType.JSON)
.post("/server/dummy")
.then()
.statusCode(HttpStatus.SC_OK)
.body(equalTo("0"));

// ContextResolver was not invoked because the types have already been cached
when().get("/server/count")
.then()
.statusCode(HttpStatus.SC_OK)
.body(equalTo("4"));

given().body("{\"Request2\":{\"value\":\"FIRST\"}}")
.contentType(ContentType.JSON)
.post("/server/dummy2")
.then()
.statusCode(HttpStatus.SC_OK)
.body(equalTo("0"));

// ContextResolver was not invoked because the types have already been cached
when().get("/server/count")
.then()
.statusCode(HttpStatus.SC_OK)
.body(equalTo("4"));
}

private static void doTest() {

}

@Path("/server")
public static class MyResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public String post(Request request) {
return request.value;
@Path("dummy")
public Dummy dummy(Request request) {
return Dummy.valueOf(request.value);
}

@POST
@Consumes(MediaType.APPLICATION_JSON)
@Path("dummy2")
public Dummy2 dummy2(Request2 request) {
return Dummy2.valueOf(request.value);
}

@GET
@Path("count")
public long count() {
return CustomObjectMapperContextResolver.COUNT.get();
}
}

public enum Dummy {
FIRST,
SECOND
}

public enum Dummy2 {
FIRST,
SECOND
}

public static class Request {
private String value;
protected String value;

public Request() {

@@ -85,14 +162,21 @@ public int hashCode() {
}
}

public static class Request2 extends Request {
}

@Provider
@Unremovable
public static class CustomObjectMapperContextResolver implements ContextResolver<ObjectMapper> {

static final AtomicLong COUNT = new AtomicLong();

@Override
public ObjectMapper getContext(final Class<?> type) {
COUNT.incrementAndGet();
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
objectMapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE)
.enable(SerializationFeature.WRITE_ENUMS_USING_INDEX);
return objectMapper;
}
}
Loading