Skip to content

Commit

Permalink
Merge pull request quarkusio#36834 from aloubyansky/3.2.8-backports-4
Browse files Browse the repository at this point in the history
3.2.8 backports 4
  • Loading branch information
aloubyansky authored Nov 3, 2023
2 parents 28c2d63 + e0619d7 commit 1789356
Show file tree
Hide file tree
Showing 36 changed files with 399 additions and 82 deletions.
2 changes: 1 addition & 1 deletion bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@
<mysql-jdbc.version>8.0.30</mysql-jdbc.version>
<mssql-jdbc.version>12.2.0.jre11</mssql-jdbc.version>
<adal4j.version>1.6.7</adal4j.version>
<oracle-jdbc.version>23.2.0.0</oracle-jdbc.version>
<oracle-jdbc.version>23.3.0.23.09</oracle-jdbc.version>
<derby-jdbc.version>10.14.2.0</derby-jdbc.version>
<db2-jdbc.version>11.5.8.0</db2-jdbc.version>
<shrinkwrap.version>1.2.6</shrinkwrap.version>
Expand Down
20 changes: 18 additions & 2 deletions devtools/maven/src/main/java/io/quarkus/maven/Deployer.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package io.quarkus.maven;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.apache.maven.model.Dependency;
import org.apache.maven.project.MavenProject;

import io.quarkus.deployment.util.DeploymentUtil;
Expand Down Expand Up @@ -76,10 +79,23 @@ public static Optional<Deployer> getDeployer(MavenProject project) {
* @return A set with the discovered extenions.
*/
public static Set<String> getProjecDeployer(MavenProject project) {
return project.getDependencies().stream()
return getProjecDeployers(project.getDependencies());
}

/**
* Get the deployer extensions found in the project.
*
* @param dependencies the dependencies for extensions
* @return A set with the discovered extenions.
*/
public static Set<String> getProjecDeployers(List<Dependency> dependencies) {
if (dependencies == null) {
return Collections.emptySet();
}
return dependencies.stream()
.filter(d -> QUARKUS_GROUP_ID.equals(d.getGroupId()))
.map(d -> strip(d.getArtifactId()))
.filter(n -> Arrays.stream(Deployer.values()).anyMatch(e -> e.equals(n)))
.filter(n -> Arrays.stream(Deployer.values()).anyMatch(e -> e.name().equals(n)))
.collect(Collectors.toSet());
}

Expand Down
60 changes: 60 additions & 0 deletions devtools/maven/src/test/java/io/quarkus/maven/DeployerTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package io.quarkus.maven;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.List;
import java.util.Set;

import org.apache.maven.model.Dependency;
import org.junit.jupiter.api.Test;

import io.quarkus.builder.Version;

public class DeployerTest {

@Test
void shouldNotFindDeployer() {
Set<String> deployers = Deployer.getProjecDeployers(List.of());
assertTrue(deployers.isEmpty());

deployers = Deployer.getProjecDeployers(List.of(newDependency("quarkus-arc"), newDependency("quarkus-resteasy")));
assertTrue(deployers.isEmpty());
}

@Test
void shouldFindDeployer() {
Set<String> deployers = Deployer.getProjecDeployers(
List.of(newDependency("quarkus-arc"), newDependency("quarkus-resteasy"), newDependency("quarkus-kubernetes")));
assertEquals(Set.of("kubernetes"), deployers);

deployers = Deployer.getProjecDeployers(
List.of(newDependency("quarkus-arc"), newDependency("quarkus-resteasy"), newDependency("quarkus-openshift")));
assertEquals(Set.of("openshift"), deployers);

deployers = Deployer.getProjecDeployers(
List.of(newDependency("quarkus-arc"), newDependency("quarkus-resteasy"), newDependency("quarkus-kind")));
assertEquals(Set.of("kind"), deployers);

deployers = Deployer.getProjecDeployers(
List.of(newDependency("quarkus-arc"), newDependency("quarkus-resteasy"), newDependency("quarkus-minikube")));
assertEquals(Set.of("minikube"), deployers);
}

@Test
void shouldFindMultipleDeployer() {
Set<String> deployers = Deployer.getProjecDeployers(
List.of(newDependency("quarkus-arc"), newDependency("quarkus-resteasy"), newDependency("quarkus-kubernetes"),
newDependency("quarkus-openshift")));
assertEquals(Set.of("kubernetes", "openshift"), deployers);
}

private static Dependency newDependency(String artifactId) {
Dependency dependency = new Dependency();
dependency.setGroupId("io.quarkus");
dependency.setArtifactId(artifactId);
dependency.setVersion(Version.getVersion());
dependency.setType("jar");
return dependency;
}
}
2 changes: 1 addition & 1 deletion docs/src/main/asciidoc/smallrye-graphql.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ The GraphQL UI can be accessed from http://localhost:8080/q/graphql-ui/ .

image:graphql-ui-screenshot01.png[alt=GraphQL UI]

Have a look at the link:security-authorization[Authorization of Web Endpoints] Guide on how to add/remove security for the GraphQL UI.
Have a look at the link:security-authorize-web-endpoints-reference[Authorization of Web Endpoints] Guide on how to add/remove security for the GraphQL UI.

== Query the GraphQL API

Expand Down
55 changes: 31 additions & 24 deletions docs/src/main/asciidoc/transaction.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@ This guide is maintained in the main Quarkus repository
and pull requests should be submitted there:
https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc
////
= Using Transactions in Quarkus
= Using transactions in Quarkus
include::_attributes.adoc[]
:categories: data
:summary: Quarkus comes with a Transaction Manager and uses it to coordinate and expose transactions to your applications. Each extension dealing with persistence will integrate with it for you, and you will explicitly interact with transactions via CDI. This guide will walk you through all that.
:diataxis-type: reference
:categories: data,getting-started
:topics: data,jpa,jta,transactions,narayana
:extensions: io.quarkus:quarkus-narayana-jta

Quarkus comes with a Transaction Manager and uses it to coordinate and expose transactions to your applications.
Each extension dealing with persistence will integrate with it for you.
And you will explicitly interact with transactions via CDI.
This guide will walk you through all that.
The `quarkus-narayana-jta` extension provides a Transaction Manager that coordinates and expose transactions to your applications as described in the link: https://jakarta.ee/specifications/transactions/[Jakarta Transactions] specification, formerly known as Java Transaction API (JTA).

When discussing Quarkus transactions, this guide refers to Jakarta Transactions transaction style and uses only the term _transaction_ to address them.

Also, Quarkus does not support distributed transactions.
This means that models that propagate transaction context, such as link:https://download.oracle.com/otndocs/jcp/7309-jts-1.0-spec-oth-JSpec/[Java Transaction Service] (JTS), REST-AT, WS-Atomic Transaction, and others, are not supported by the `narayana-jta` extension.

== Setting it up

Expand Down Expand Up @@ -120,7 +124,7 @@ public class SantaClausService {
<2> Programmatically decide to set the transaction for rollback.


=== Transaction Configuration
=== Transaction configuration

Advanced configuration of the transaction is possible with the use of the `@TransactionConfiguration` annotation that is set in addition to the standard `@Transactional` annotation on your entry method or at the class level.

Expand All @@ -137,8 +141,8 @@ The configuration defined on a method takes precedence over the configuration de
If your `@Transactional`-annotated method returns a reactive value, such as:

- `CompletionStage` (from the JDK)
- `Publisher` (from Reactive-Streams)
- Any type which can be converted to one of the two previous types using Reactive Type Converters
- `Publisher` (from Reactive-Streams)
- Any type that can be converted to one of the two previous types using Reactive Type Converters

then the behaviour is a bit different, because the transaction will not be terminated until the
returned reactive value is terminated. In effect, the returned reactive value will be listened to
Expand All @@ -151,7 +155,7 @@ work is really done, and not just until the reactive method returns.
If you need to propagate your transaction context across your reactive pipeline, please see the
xref:context-propagation.adoc[Context Propagation guide].

=== Programmatic Approach
=== Programmatic approach

You can use static methods on `QuarkusTransaction` to define transaction boundaries. This provides two different options,
a functional approach that allows you to run a lambda within the scope of a transaction, or by using explicit `begin`,
Expand Down Expand Up @@ -365,37 +369,41 @@ which is a CDI bean and can be ``@Inject``ed.
[[jdbcstore]]
== Configure storing of Quarkus transaction logs in a database

In cloud environments where persistent storage is not available, such as when application containers are unable to use persistent volumes, you can configure the transaction management to store transaction logs in a database by using a JDBC datasource.

IMPORTANT: While there are several benefits to using a database to store transaction logs, you might notice a reduction in performance compared with using the file system to store the logs.
In cloud environments where persistent storage is not available, such as when application containers are unable to use persistent volumes, you can configure the transaction management to store transaction logs in a database by using a Java Database Connectivity (JDBC) datasource.

Quarkus allows the following JDBC-specific configuration of the object store included in `quarkus.transacion-manager.object-store.<property>` properties, where <property> can be:
However, in cloud-native apps, using a database to store transaction logs has additional requirements.
The `narayana-jta` extension, which manages these transactions, requires stable storage, a unique reusable node identifier, and a steady IP address to work correctly.
While the JDBC object store provides a stable storage, users must still plan how to meet the other two requirements.

Quarkus, after you evaluate whether using a database to store transaction logs is right for you, allows the following JDBC-specific configuration of the object store included in `quarkus.transaction-manager.object-store._<property>_` properties, where _<property>_ can be:

* `type` (_string_): Configure this property to `jdbc` to enable usage of a Quarkus JDBC datasource for transaction logging.
* `type` (_string_): Configure this property to `jdbc` to enable usage of a Quarkus JDBC datasource for storing transaction logs.
The default value is `file-system`.

* `datasource` (_string_): Specify the name of the datasource for the transaction log storage.
If no value is provided for the `datasource` property, Quarkus uses the xref:datasource.adoc#configure-datasources[default datasource].

* `create-table` (_boolean_): When set to `true`, the transaction log table gets automatically created if it does not already exist.
The default value is `false`.

* `drop-table` (_boolean_): When set to `true`, the tables are dropped on startup if they already exist.
The default value is `false`.

* `table-prefix` (string): Specify the prefix for a related table name.
The default value is `quarkus_`.

// This paragraph will differ from the RHBQ docs in the future since it is not supported in the product yet.
Additional information:
For more configuration information, see the *Narayana JTA - Transaction manager* section of the Quarkus xref:all-config.adoc[All configuration options] reference.

* You can manually create the transaction log table during the initial setup by setting the `create-table` property to `true`.
.Additional information:

* Create the transaction log table during the initial setup by setting the `create-table` property to `true`.

* JDBC datasources and ActiveMQ Artemis allow the enlistment and automatically register the `XAResourceRecovery`.

** JDBC datasources is part of `quarkus-agroal`, and it needs to use `quarkus.datasource.jdbc.transactions=XA`.
** ActiveMQ Artemis is part of `quarkus-pooled-jms`, and it needs to use `quarkus.pooled-jms.transaction=XA`.
+
For more information, see link:https://issues.redhat.com/browse/CEQ-4878[CEQ-4878].

* If your application employs eXtended Architecture (XA) transactions, enable the transaction crash recovery feature by using `quarkus.transaction-manager.enable-recovery=true`, to safeguard your data in the event of application crashes or failures. The default value for XA Recovery is `false`.
* To ensure data integrity in case of application crashes or failures, enable the transaction crash recovery with the `quarkus.transaction-manager.enable-recovery=true` configuration.

[NOTE]
====
Expand All @@ -408,7 +416,6 @@ quarkus.datasource.TX_LOG.jdbc.transactions=disabled
This example uses TX_LOG as the datasource name.
====


== Why always having a transaction manager?

Does it work everywhere I want to?::
Expand Down Expand Up @@ -445,6 +452,6 @@ Transactions are also about JMS and other database access, so one API makes more
It's a mess because I don't know if my Jakarta Persistence persistence unit is using `JTA` or `Resource-level` Transaction::

It's not a mess in Quarkus :)
Resource-level was introduced to support Jakarta Persistence in a non managed environment.
Resource-level was introduced to support Jakarta Persistence in a non-managed environment.
But Quarkus is both lean and a managed environment, so we can safely always assume we are in JTA mode.
The end result is that the difficulties of running Hibernate ORM + CDI + a transaction manager in Java SE mode are solved by Quarkus.
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
package io.quarkus.jdbc.db2.deployment;

import com.ibm.db2.jcc.resources.ResourceKeys;
import com.ibm.db2.jcc.resources.Resources;
import com.ibm.db2.jcc.resources.SqljResources;
import com.ibm.db2.jcc.resources.T2uResourceKeys;
import com.ibm.db2.jcc.resources.T2uResources;
import com.ibm.db2.jcc.resources.T2zResourceKeys;
import com.ibm.db2.jcc.resources.T2zResources;
import com.ibm.db2.jcc.resources.T4ResourceKeys;
import com.ibm.db2.jcc.resources.T4Resources;

import io.quarkus.agroal.spi.JdbcDriverBuildItem;
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.processor.BuiltinScope;
Expand Down Expand Up @@ -55,12 +65,23 @@ void configureAgroalConnection(BuildProducer<AdditionalBeanBuildItem> additional
}

@BuildStep
void registerDriverForReflection(BuildProducer<ReflectiveClassBuildItem> reflectiveClass) {
void registerForReflection(BuildProducer<ReflectiveClassBuildItem> reflectiveClass) {
//Not strictly necessary when using Agroal, as it also registers
//any JDBC driver being configured explicitly through its configuration.
//We register it for the sake of people not using Agroal,
//for example when the driver is used with OpenTelemetry JDBC instrumentation.
reflectiveClass.produce(ReflectiveClassBuildItem.builder(DB2_DRIVER_CLASS).build());

// register resource bundles for reflection (they are apparently classes...)
reflectiveClass.produce(ReflectiveClassBuildItem.builder(Resources.class).build());
reflectiveClass.produce(ReflectiveClassBuildItem.builder(ResourceKeys.class).build());
reflectiveClass.produce(ReflectiveClassBuildItem.builder(SqljResources.class).build());
reflectiveClass.produce(ReflectiveClassBuildItem.builder(T2uResourceKeys.class).build());
reflectiveClass.produce(ReflectiveClassBuildItem.builder(T2uResources.class).build());
reflectiveClass.produce(ReflectiveClassBuildItem.builder(T2zResourceKeys.class).build());
reflectiveClass.produce(ReflectiveClassBuildItem.builder(T2zResources.class).build());
reflectiveClass.produce(ReflectiveClassBuildItem.builder(T4ResourceKeys.class).build());
reflectiveClass.produce(ReflectiveClassBuildItem.builder(T4Resources.class).build());
}

@BuildStep
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.quarkus.liquibase.runtime.graal;

import java.util.Map;

import com.oracle.svm.core.annotate.Delete;
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;

@TargetClass(className = "liquibase.configuration.core.EnvironmentValueProvider")
final class SubstituteEnvironmentValueProvider {

@Delete
private Map<String, String> environment;

@Substitute
protected Map<?, ?> getMap() {
return System.getenv();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ public IsZstdAbsent() {
try {
Class.forName("com.github.luben.zstd.Zstd");
zstdAbsent = false;
} catch (ClassNotFoundException e) {
} catch (Exception e) {
// It can be a classloading issue (the library is not available), or a native issue
// (the library for the current OS/arch is not available)
zstdAbsent = true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,13 @@ public class AccessTokenRequestReactiveFilter implements ResteasyReactiveClientR

@PostConstruct
public void initExchangeTokenClient() {
if (exchangeToken) {
if (isExchangeToken()) {
OidcClients clients = Arc.container().instance(OidcClients.class).get();
exchangeTokenClient = oidcClientName.isPresent() ? clients.getClient(oidcClientName.get()) : clients.getClient();
String clientName = getClientName();
exchangeTokenClient = clientName != null ? clients.getClient(clientName) : clients.getClient();
Grant.Type exchangeTokenGrantType = ConfigProvider.getConfig()
.getValue(
"quarkus.oidc-client." + (oidcClientName.isPresent() ? oidcClientName.get() + "." : "")
"quarkus.oidc-client." + (clientName != null ? clientName + "." : "")
+ "grant.type",
Grant.Type.class);
if (exchangeTokenGrantType == Grant.Type.EXCHANGE) {
Expand All @@ -66,6 +67,10 @@ public void initExchangeTokenClient() {
}
}

protected boolean isExchangeToken() {
return exchangeToken;
}

@Override
public void filter(ResteasyReactiveClientRequestContext requestContext) {
if (verifyTokenInstance(requestContext)) {
Expand Down Expand Up @@ -101,6 +106,10 @@ public void accept(Throwable t) {
}
}

protected String getClientName() {
return oidcClientName.orElse(null);
}

public void propagateToken(ResteasyReactiveClientRequestContext requestContext, String accessToken) {
if (accessToken != null) {
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, BEARER_SCHEME_WITH_SPACE + accessToken);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.quarkus.oidc.token.propagation;

public class CustomAccessTokenRequestFilter extends AccessTokenRequestFilter {

@Override
protected String getClientName() {
return "exchange";
}

@Override
protected boolean isExchangeToken() {
return true;
}

}
Loading

0 comments on commit 1789356

Please sign in to comment.