Skip to content

Commit

Permalink
Merge pull request #40792 from gsmet/3.8.5-backports-4
Browse files Browse the repository at this point in the history
[3.8] 3.8.5 backports 4
  • Loading branch information
gsmet authored Jun 4, 2024
2 parents 3ce356d + 804e0c3 commit b6b00e9
Show file tree
Hide file tree
Showing 88 changed files with 1,620 additions and 271 deletions.
6 changes: 3 additions & 3 deletions .github/native-tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,9 @@
"os-name": "ubuntu-latest"
},
{
"category": "Windows - RESTEasy Jackson",
"timeout": 25,
"test-modules": "resteasy-jackson",
"category": "Windows support",
"timeout": 50,
"test-modules": "resteasy-jackson, qute",
"os-name": "windows-latest"
},
{
Expand Down
2 changes: 1 addition & 1 deletion bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@
<jna.version>5.8.0</jna.version><!-- should satisfy both testcontainers and mongodb -->
<antlr.version>4.13.0</antlr.version><!-- needs to align with same property in build-parent/pom.xml -->
<quarkus-security.version>2.0.3.Final</quarkus-security.version>
<keycloak.version>23.0.7</keycloak.version>
<keycloak.version>24.0.4</keycloak.version>
<logstash-gelf.version>1.15.1</logstash-gelf.version>
<checker-qual.version>3.42.0</checker-qual.version>
<error-prone-annotations.version>2.24.0</error-prone-annotations.version>
Expand Down
2 changes: 1 addition & 1 deletion build-parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@

<!-- The image to use for tests that run Keycloak -->
<!-- IMPORTANT: If this is changed you must also update bom/application/pom.xml and KeycloakBuildTimeConfig/DevServicesConfig in quarkus-oidc/deployment to match the version -->
<keycloak.version>23.0.7</keycloak.version>
<keycloak.version>24.0.4</keycloak.version>
<keycloak.wildfly.version>19.0.3</keycloak.wildfly.version>
<keycloak.docker.image>quay.io/keycloak/keycloak:${keycloak.version}</keycloak.docker.image>
<keycloak.docker.legacy.image>quay.io/keycloak/keycloak:${keycloak.wildfly.version}-legacy</keycloak.docker.legacy.image>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import io.quarkus.deployment.annotations.BuildProducer;
Expand Down Expand Up @@ -46,8 +45,8 @@ public void set(List<SetClassPathSystemPropBuildItem> setCPItems,

}
}
String classPathValue = Stream.concat(parentFirst.stream(), regular.stream()).map(p -> p.toAbsolutePath().toString())
.collect(Collectors.joining(":"));
recorder.set(classPathValue);
List<String> allJarPaths = Stream.concat(parentFirst.stream(), regular.stream()).map(p -> p.toAbsolutePath().toString())
.toList();
recorder.set(allJarPaths);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package io.quarkus.runtime;

import java.util.List;

import io.quarkus.runtime.annotations.Recorder;

@Recorder
public class ClassPathSystemPropertyRecorder {

public void set(String value) {
System.setProperty("java.class.path", value);
public void set(List<String> allJarPaths) {
System.setProperty("java.class.path", String.join(":", allJarPaths));
}
}
69 changes: 40 additions & 29 deletions devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
Expand Down Expand Up @@ -745,15 +744,15 @@ private void executeGoal(PluginExec pluginExec, String goal, Map<String, String>

private List<String> readAnnotationProcessors(Xpp3Dom pluginConfig) {
if (pluginConfig == null) {
return Collections.emptyList();
return List.of();
}
Xpp3Dom annotationProcessors = pluginConfig.getChild("annotationProcessors");
if (annotationProcessors == null) {
return Collections.emptyList();
return List.of();
}
Xpp3Dom[] processors = annotationProcessors.getChildren("annotationProcessor");
if (processors.length == 0) {
return Collections.emptyList();
return List.of();
}
List<String> ret = new ArrayList<>(processors.length);
for (Xpp3Dom processor : processors) {
Expand All @@ -764,21 +763,18 @@ private List<String> readAnnotationProcessors(Xpp3Dom pluginConfig) {

private Set<File> readAnnotationProcessorPaths(Xpp3Dom pluginConfig) throws MojoExecutionException {
if (pluginConfig == null) {
return Collections.emptySet();
return Set.of();
}
Xpp3Dom annotationProcessorPaths = pluginConfig.getChild("annotationProcessorPaths");
if (annotationProcessorPaths == null) {
return Collections.emptySet();
return Set.of();
}
var versionConstraints = getAnnotationProcessorPathsDepMgmt(pluginConfig);
Xpp3Dom[] paths = annotationProcessorPaths.getChildren("path");
Set<File> elements = new LinkedHashSet<>();
try {
List<org.eclipse.aether.graph.Dependency> dependencies = convertToDependencies(paths);
// NOTE: The Maven Compiler Plugin also supports a flag (disabled by default) for applying managed dependencies to
// the dependencies of the APT plugins (not them directly), which we don't support yet here
// you can find the implementation at https://github.com/apache/maven-compiler-plugin/pull/180/files#diff-d4bac42d8f4c68d397ddbaa05c1cbbed7984ef6dc0bb9ea60739df78997e99eeR1610
// when/if we need it
CollectRequest collectRequest = new CollectRequest(dependencies, Collections.emptyList(),
CollectRequest collectRequest = new CollectRequest(dependencies, versionConstraints,
project.getRemoteProjectRepositories());
DependencyRequest dependencyRequest = new DependencyRequest();
dependencyRequest.setCollectRequest(collectRequest);
Expand All @@ -795,6 +791,18 @@ private Set<File> readAnnotationProcessorPaths(Xpp3Dom pluginConfig) throws Mojo
}
}

private List<org.eclipse.aether.graph.Dependency> getAnnotationProcessorPathsDepMgmt(Xpp3Dom pluginConfig) {
final Xpp3Dom useDepMgmt = pluginConfig.getChild("annotationProcessorPathsUseDepMgmt");
if (useDepMgmt == null || !Boolean.parseBoolean(useDepMgmt.getValue())) {
return List.of();
}
var dm = project.getDependencyManagement();
if (dm == null) {
return List.of();
}
return getProjectAetherDependencyManagement();
}

private List<org.eclipse.aether.graph.Dependency> convertToDependencies(Xpp3Dom[] paths) throws MojoExecutionException {
List<org.eclipse.aether.graph.Dependency> dependencies = new ArrayList<>();
for (Xpp3Dom path : paths) {
Expand Down Expand Up @@ -847,7 +855,7 @@ private String toNullIfEmpty(String value) {
private List<Dependency> getProjectManagedDependencies() {
DependencyManagement dependencyManagement = project.getDependencyManagement();
if (dependencyManagement == null || dependencyManagement.getDependencies() == null) {
return Collections.emptyList();
return List.of();
}
return dependencyManagement.getDependencies();
}
Expand All @@ -863,7 +871,7 @@ private String getValue(Xpp3Dom path, String element, String defaultValue) {

private Set<org.eclipse.aether.graph.Exclusion> convertToAetherExclusions(Xpp3Dom exclusions) {
if (exclusions == null) {
return Collections.emptySet();
return Set.of();
}
Set<Exclusion> aetherExclusions = new HashSet<>();
for (Xpp3Dom exclusion : exclusions.getChildren("exclusion")) {
Expand Down Expand Up @@ -1487,21 +1495,6 @@ private void addQuarkusDevModeDeps(MavenDevModeLauncher.Builder builder, Applica
throw new MojoExecutionException("Classpath resource " + pomPropsPath + " is missing version");
}

final List<org.eclipse.aether.graph.Dependency> managed = new ArrayList<>(
project.getDependencyManagement().getDependencies().size());
project.getDependencyManagement().getDependencies().forEach(d -> {
final List<Exclusion> exclusions;
if (!d.getExclusions().isEmpty()) {
exclusions = new ArrayList<>(d.getExclusions().size());
d.getExclusions().forEach(e -> exclusions.add(new Exclusion(e.getGroupId(), e.getArtifactId(), "*", "*")));
} else {
exclusions = List.of();
}
managed.add(new org.eclipse.aether.graph.Dependency(
new DefaultArtifact(d.getGroupId(), d.getArtifactId(), d.getClassifier(), d.getType(), d.getVersion()),
d.getScope(), d.isOptional(), exclusions));
});

final DefaultArtifact devModeJar = new DefaultArtifact(devModeGroupId, devModeArtifactId, ArtifactCoords.TYPE_JAR,
devModeVersion);
final DependencyResult cpRes = repoSystem.resolveDependencies(repoSession,
Expand All @@ -1511,7 +1504,7 @@ private void addQuarkusDevModeDeps(MavenDevModeLauncher.Builder builder, Applica
// it doesn't matter what the root artifact is, it's an alias
.setRootArtifact(new DefaultArtifact(IO_QUARKUS, "quarkus-devmode-alias",
ArtifactCoords.TYPE_JAR, "1.0"))
.setManagedDependencies(managed)
.setManagedDependencies(getProjectAetherDependencyManagement())
.setDependencies(List.of(
new org.eclipse.aether.graph.Dependency(devModeJar, JavaScopes.RUNTIME),
new org.eclipse.aether.graph.Dependency(new DefaultArtifact(
Expand All @@ -1537,6 +1530,24 @@ private void addQuarkusDevModeDeps(MavenDevModeLauncher.Builder builder, Applica
}
}

private List<org.eclipse.aether.graph.Dependency> getProjectAetherDependencyManagement() {
final List<org.eclipse.aether.graph.Dependency> managed = new ArrayList<>(
project.getDependencyManagement().getDependencies().size());
project.getDependencyManagement().getDependencies().forEach(d -> {
final List<Exclusion> exclusions;
if (!d.getExclusions().isEmpty()) {
exclusions = new ArrayList<>(d.getExclusions().size());
d.getExclusions().forEach(e -> exclusions.add(new Exclusion(e.getGroupId(), e.getArtifactId(), "*", "*")));
} else {
exclusions = List.of();
}
managed.add(new org.eclipse.aether.graph.Dependency(
new DefaultArtifact(d.getGroupId(), d.getArtifactId(), d.getClassifier(), d.getType(), d.getVersion()),
d.getScope(), d.isOptional(), exclusions));
});
return managed;
}

private void setKotlinSpecificFlags(MavenDevModeLauncher.Builder builder) {
Plugin kotlinMavenPlugin = null;
for (Plugin plugin : project.getBuildPlugins()) {
Expand Down
69 changes: 69 additions & 0 deletions docs/src/main/asciidoc/datasource.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,75 @@ public class MyProducer {
----
====

[[datasource-multiple-single-transaction]]
=== Use multiple datasources in a single transaction

By default, XA support on datasources is disabled,
and thus a transaction may include at most one datasource.
Attempting to access multiple non-XA datasources in the same transaction
is unsafe and will result in a warning.

In Quarkus 3.10+, this will result in an error by default.

To safely allow using multiple JDBC datasources in the same transaction:

. Make sure your JDBC driver supports XA.
All <<extensions-and-database-drivers-reference,supported JDBC drivers do>>,
but <<other-databases,other JDBC drivers>> might not.
. Make sure your database server is configured to enable XA.
. Enable XA support explicitly for each relevant datasource by setting
<<quarkus-agroal_quarkus-datasource-jdbc-transactions,`quarkus.datasource[.optional name].transactions`>> to `xa`.

Using XA, a rollback in one datasource will trigger a rollback in every other datasource enrolled in the transaction.

[NOTE]
====
XA transactions on reactive datasources are not supported at the moment.
====

[NOTE]
====
If your transaction involves other, non-datasource resources,
keep in mind *those* resources might not support XA transactions,
or might require additional configuration.
====

If XA cannot be enabled for one of your datasources:

* Be aware that enabling XA for all datasources _except one_ (and only one) is still supported
through https://www.narayana.io/docs/project/index.html#d5e857[Last Resource Commit Optimization (LRCO)].
* If you do not need a rollback for one datasource to trigger a rollback for other datasources,
consider splitting your code into multiple transactions.
To that end, use xref:transaction.adoc#programmatic-approach[`QuarkusTransaction.requiringNew()`]/xref:transaction.adoc#declarative-approach[`@Transactional(REQUIRES_NEW)`] (preferably)
or xref:transaction.adoc#legacy-api-approach[`UserTransaction`] (for more complex use cases).

[CAUTION]
====
If you want to make sure it is impossible to access multiple non-XA datasources in the same transaction,
you can set `quarkus.transaction-manager.unsafe-multiple-last-resources` to `fail`.
This value is the default for Quarkus 3.10+.
With any value different from `fail`, a transaction rollback
could possibly be applied to only some of the non-XA datasources,
with other non-XA datasources having already committed their changes,
leaving your overall system in an inconsistent state.
By default in Quarkus 3.8, for compatibility reasons,
you get warned once for the first occurrence (`warn-first`).
You may fully allow unsafe transaction handling across multiple non-XA datasources
by setting `quarkus.transaction-manager.unsafe-multiple-last-resources` to `allow`.
Alternatively, you can allow the same unsafe behavior
but logging a warning on *each* offending transaction by setting the property to `warn-each`.
We do not recommend using this configuration property,
and we plan to remove it in the future,
so you should plan fixing your application accordingly.
If you think your use case of this feature is valid and this option should be kept around,
open an issue in the https://github.com/quarkusio/quarkus/issues/new?assignees=&labels=kind%2Fenhancement&projects=&template=feature_request.yml[Quarkus tracker]
explaining why.
====

== Datasource integrations

=== Datasource health check
Expand Down
2 changes: 1 addition & 1 deletion docs/src/main/asciidoc/hibernate-reactive.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ and will have it use the default datasource.
The configuration properties listed here allow you to override such defaults, and customize and tune various aspects.

Hibernate Reactive uses the same properties you would use for Hibernate ORM. You will notice that some properties
contain `jdbc` in the name but there is not JDBC in Hibernate Reactive, these are simply legacy property names.
contain `jdbc` in the name but there is no JDBC in Hibernate Reactive, these are simply legacy property names.

include::{generated-dir}/config/quarkus-hibernate-orm.adoc[opts=optional, leveloffset=+2]

Expand Down
21 changes: 21 additions & 0 deletions docs/src/main/asciidoc/resteasy-reactive.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1340,8 +1340,12 @@ A very simple Jakarta REST Resource that uses `Person` could be:
----
package org.acme.rest;
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Response;
@Path("person")
public class Person {
Expand All @@ -1351,8 +1355,25 @@ public class Person {
public Person getPerson(Long id) {
return new Person(id, "foo", "bar", "Brick Lane");
}
@Produces(APPLICATION_JSON) <1>
@Path("/friend/{id}")
@GET
public Response getPersonFriend(Long id) {
var person = new Person(id, "foo", "bar", "Brick Lane");
return Response.ok(person).build();
}
}
----
<1> The `@SecureField` annotation is only effective when Quarkus recognizes that produced content type is the 'application/json' type.

WARNING: Currently you cannot use the `@SecureField` annotation to secure your data returned from resource methods returning the `io.smallrye.mutiny.Multi` reactive type.

[IMPORTANT]
====
All resource methods returning data secured with the `@SecureField` annotation should be tested.
Please make sure data are secured as you intended.
====

Assuming security has been set up for the application (see our xref:security-overview.adoc[guide] for more details), when a user with the `admin` role
performs an HTTP GET on `/person/1` they will receive:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ For more information, see xref:security-oidc-bearer-token-authentication.adoc#in
[[keycloak-initialization]]
=== Keycloak initialization

The `quay.io/keycloak/keycloak:23.0.7` image which contains a Keycloak distribution powered by Quarkus is used to start a container by default.
The `quay.io/keycloak/keycloak:24.0.4` image which contains a Keycloak distribution powered by Quarkus is used to start a container by default.
`quarkus.keycloak.devservices.image-name` can be used to change the Keycloak image name.
For example, set it to `quay.io/keycloak/keycloak:19.0.3-legacy` to use a Keycloak distribution powered by WildFly.
Be aware that a Quarkus-based Keycloak distribution is only available starting from Keycloak `20.0.0`.
Expand Down
6 changes: 3 additions & 3 deletions docs/sync-web-site.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ if [ -z $TARGET_DIR ]; then
GIT_OPTIONS="--depth=1"
fi
if [ -n "${RELEASE_GITHUB_TOKEN}" ]; then
git clone -b develop --single-branch $GIT_OPTIONS https://github.com/quarkusio/quarkusio.github.io.git ${TARGET_DIR}
git clone --single-branch $GIT_OPTIONS https://github.com/quarkusio/quarkusio.github.io.git ${TARGET_DIR}
else
git clone -b develop --single-branch $GIT_OPTIONS [email protected]:quarkusio/quarkusio.github.io.git ${TARGET_DIR}
git clone --single-branch $GIT_OPTIONS [email protected]:quarkusio/quarkusio.github.io.git ${TARGET_DIR}
fi
fi

Expand Down Expand Up @@ -143,7 +143,7 @@ then
cd target/web-site
git add -A
git commit -m "Sync web site with Quarkus documentation"
git push origin develop
git push origin main
echo "Web Site updated - wait for CI build"
else
echo "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.io.ByteArrayOutputStream;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
Expand Down Expand Up @@ -62,7 +63,7 @@ protected HttpResponseMessage nettyDispatch(HttpRequestMessage<Optional<String>>

HttpContent requestContent = LastHttpContent.EMPTY_LAST_CONTENT;
if (request.getBody().isPresent()) {
ByteBuf body = Unpooled.wrappedBuffer(request.getBody().get().getBytes());
ByteBuf body = Unpooled.wrappedBuffer(request.getBody().get().getBytes(StandardCharsets.UTF_8));
requestContent = new DefaultLastHttpContent(body);
}

Expand Down
Loading

0 comments on commit b6b00e9

Please sign in to comment.