Skip to content

Commit

Permalink
Merge branch 'main' into issue-30101
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobdotcosta authored Jan 4, 2023
2 parents 2f7e5c7 + 3e7eb23 commit eb397d4
Show file tree
Hide file tree
Showing 74 changed files with 1,590 additions and 237 deletions.
4 changes: 2 additions & 2 deletions bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@
<kotlin.coroutine.version>1.6.4</kotlin.coroutine.version>
<kotlin-serialization.version>1.4.1</kotlin-serialization.version>
<kubernetes-client.version>6.3.1</kubernetes-client.version> <!-- Please check with Java Operator SDK team before updating -->
<dekorate.version>3.1.3</dekorate.version> <!-- Please check with Java Operator SDK team before updating -->
<dekorate.version>3.2.0</dekorate.version> <!-- Please check with Java Operator SDK team before updating -->
<maven-invoker.version>3.2.0</maven-invoker.version>
<awaitility.version>4.2.0</awaitility.version>
<jboss-logmanager.version>1.0.11</jboss-logmanager.version>
Expand Down Expand Up @@ -207,7 +207,7 @@
<aesh.version>2.6</aesh.version>
<!-- these two artifacts needs to be compatible together -->
<strimzi-oauth.version>0.11.0</strimzi-oauth.version>
<strimzi-oauth.nimbus.version>9.25.6</strimzi-oauth.nimbus.version>
<strimzi-oauth.nimbus.version>9.27</strimzi-oauth.nimbus.version>
<java-buildpack-client.version>0.0.6</java-buildpack-client.version>
<org-crac.version>0.1.3</org-crac.version>
<sshd-common.version>2.9.2</sshd-common.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 @@ -107,7 +107,7 @@
<assertj.version>3.23.1</assertj.version>

<wiremock-jre8.version>2.35.0</wiremock-jre8.version>
<wiremock-maven-plugin.version>7.2.0</wiremock-maven-plugin.version>
<wiremock-maven-plugin.version>7.3.0</wiremock-maven-plugin.version>

<!-- Artemis test dependencies -->
<artemis.version>2.27.1</artemis.version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public Result get() {
.loadClass("org.testcontainers.utility.TestcontainersConfiguration");
Object configurationInstance = configurationClass.getMethod("getInstance").invoke(null);
String oldReusePropertyValue = (String) configurationClass
.getMethod("getUserProperty", String.class, String.class)
.getMethod("getEnvVarOrUserProperty", String.class, String.class)
.invoke(configurationInstance, "testcontainers.reuse.enable", "false"); // use the default provided in TestcontainersConfiguration#environmentSupportsReuse
Method updateUserConfigMethod = configurationClass.getMethod("updateUserConfig", String.class, String.class);
// this will ensure that testcontainers does not start ryuk - see https://github.com/quarkusio/quarkus/issues/25852 for why this is important
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public A build(ClassOutput classOutput) {

String name = annotationInstance.name().toString();

// Ljavax/enterprise/util/AnnotationLiteral<Lcom/foo/MyAnnotation;>;Lcom/foo/MyAnnotation;
// Ljakarta/enterprise/util/AnnotationLiteral<Lcom/foo/MyAnnotation;>;Lcom/foo/MyAnnotation;
String signature = String.format("L%1$s<L%2$s;>;L%2$s;",
AnnotationLiteral.class.getName().replace('.', '/'),
name.replace('.', '/'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
import io.quarkus.runtime.StartupTask;
import io.quarkus.runtime.annotations.QuarkusMain;
import io.quarkus.runtime.appcds.AppCDSUtil;
import io.quarkus.runtime.configuration.ConfigUtils;
import io.quarkus.runtime.configuration.ProfileManager;
import io.quarkus.runtime.naming.DisabledInitialContextManager;
import io.quarkus.runtime.util.StepTiming;
Expand Down Expand Up @@ -251,8 +252,8 @@ void build(List<StaticBytecodeRecorderBuildItem> staticInitTasks,
startupContext, mv.getMethodParam(0));

mv.invokeStaticMethod(CONFIGURE_STEP_TIME_ENABLED);
ResultHandle activeProfile = mv
.invokeStaticMethod(ofMethod(ProfileManager.class, "getActiveProfile", String.class));
ResultHandle profiles = mv
.invokeStaticMethod(ofMethod(ConfigUtils.class, "getProfiles", List.class));

tryBlock = mv.tryBlock();
tryBlock.invokeStaticMethod(CONFIGURE_STEP_TIME_START);
Expand All @@ -274,12 +275,12 @@ void build(List<StaticBytecodeRecorderBuildItem> staticInitTasks,
ResultHandle featuresHandle = tryBlock.load(featureNames.stream().sorted().collect(Collectors.joining(", ")));
tryBlock.invokeStaticMethod(
ofMethod(Timing.class, "printStartupTime", void.class, String.class, String.class, String.class, String.class,
String.class, boolean.class, boolean.class),
List.class, boolean.class, boolean.class),
tryBlock.load(applicationInfo.getName()),
tryBlock.load(applicationInfo.getVersion()),
tryBlock.load(Version.getVersion()),
featuresHandle,
activeProfile,
profiles,
tryBlock.load(LaunchMode.DEVELOPMENT.equals(launchMode.getLaunchMode())),
tryBlock.load(launchMode.isAuxiliaryApplication()));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ private void addLocalProject(ResolvedDependency project, GradleDevModeLauncher.B
}
}
Path classesDir = classesDirs.isEmpty() ? null
: QuarkusGradleUtils.mergeClassesDirs(classesDirs, project.getWorkspaceModule().getBuildDir(), root, root);
: QuarkusGradleUtils.mergeClassesDirs(classesDirs, project.getWorkspaceModule().getBuildDir(), root, false);

final Set<Path> resourcesSrcDirs = new LinkedHashSet<>();
// resourcesSrcDir may exist but if it's empty the resources output dir won't be created
Expand Down
7 changes: 1 addition & 6 deletions docs/src/main/asciidoc/config-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -297,14 +297,9 @@ quarkus.http.port=9090

Setting `quarkus.profile` to `staging` will activate the `staging` profile.

IMPORTANT: Only a single profile may be active at a time.

[NOTE]
====
The `io.quarkus.runtime.configuration.ProfileManager#getActiveProfile` API provides a way to retrieve the active profile
programmatically.
Using `@ConfigProperty("quarkus.profile")` will *not* work properly.
The `io.smallrye.config.SmallRyeConfig#getProfiles` API provides a way to retrieve the active profiles programmatically.
====

=== Profile aware files
Expand Down
25 changes: 23 additions & 2 deletions docs/src/main/asciidoc/resteasy-reactive.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2637,12 +2637,14 @@ TIP: By default, the following list of media types is compressed: `text/html`, `
NOTE: If the client does not support HTTP compression then the response body is not compressed.


== Include/Exclude JAX-RS classes with build time conditions
== Include/Exclude JAX-RS classes

=== Using Build time conditions

Quarkus enables the inclusion or exclusion of JAX-RS Resources, Providers and Features directly thanks to build time conditions in the same that it does for CDI beans.
Thus, the various JAX-RS classes can be annotated with profile conditions (`@io.quarkus.arc.profile.IfBuildProfile` or `@io.quarkus.arc.profile.UnlessBuildProfile`) and/or with property conditions (`io.quarkus.arc.properties.IfBuildProperty` or `io.quarkus.arc.properties.UnlessBuildProperty`) to indicate to Quarkus at build time under which conditions these JAX-RS classes should be included.

In the following example, Quarkus includes the endpoint `sayHello` if and only if the build profile `app1` has been enabled.
In the following example, Quarkus includes the `ResourceForApp1Only` Resource class if and only if the build profile `app1` has been enabled.

[source,java]
----
Expand All @@ -2659,6 +2661,25 @@ public class ResourceForApp1Only {

Please note that if a JAX-RS Application has been detected and the method `getClasses()` and/or `getSingletons()` has/have been overridden, Quarkus will ignore the build time conditions and consider only what has been defined in the JAX-RS Application.

=== Using a runtime property

Quarkus can also conditionally disable JAX-RS Resources based on the value of runtime properties using the `@io.quarkus.resteasy.reactive.server.EndpointDisabled` annotation.

In the following example, Quarkus will exclude `RuntimeResource` at runtime if the application has `some.property` configured to `"disable"`.

[source,java]
----
@EndpointDisabled(name = "some.property", stringValue = "disable")
public class RuntimeResource {
@GET
@Path("sayHello")
public String sayHello() {
return "hello";
}
}
----


== RESTEasy Reactive client

Expand Down
18 changes: 17 additions & 1 deletion docs/src/main/asciidoc/security-jwt.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -811,11 +811,27 @@ If you work with Keycloak and configure `mp.jwt.verify.publickey.location` to po

[source, properties]
----
# keycloak.url is set by OidcWiremockTestResource
# keycloak.url is set by DevServices for Keycloak
mp.jwt.verify.publickey.location=${keycloak.url}/realms/quarkus/protocol/openid-connect/certs
mp.jwt.verify.issuer=${keycloak.url}/realms/quarkus
----

Note that the tokens issued by Keycloak have an `iss` (issuer) claim set to the realm endpoint address.

If your Quarkus application is running in a docker container then it may share a network interface with a Keycloak docker container launched by DevServices for Keycloak, with the Quarkus application and Keycloak communicating with each other via an internal shared docker network.

In such cases, use the following configuration instead:

[source, properties]
----
# keycloak.url is set by DevServices for Keycloak,
# Quarkus will access it via an internal shared docker network interface.
mp.jwt.verify.publickey.location=${keycloak.url}/realms/quarkus/protocol/openid-connect/certs
# Issuer is set to the docker bridge localhost endpoint address represented by the `client.quarkus.oidc.auth-server-url` property
mp.jwt.verify.issuer=${client.quarkus.oidc.auth-server-url}
----

[[integration-testing-public-key]]
==== Local Public Key

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package io.quarkus.arc.deployment;

import static java.util.Arrays.asList;
import static java.util.Collections.emptySet;
import static java.util.function.Predicate.not;
import static java.util.stream.Collectors.groupingBy;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -57,10 +60,9 @@ public class BuildTimeEnabledProcessor {

@BuildStep
void ifBuildProfile(CombinedIndexBuildItem index, BuildProducer<BuildTimeConditionBuildItem> producer) {
Collection<AnnotationInstance> annotationInstances = index.getIndex().getAnnotations(IF_BUILD_PROFILE);
List<AnnotationInstance> annotationInstances = getAnnotations(index.getIndex(), IF_BUILD_PROFILE);
for (AnnotationInstance instance : annotationInstances) {
String profileOnInstance = instance.value().asString();
boolean enabled = ConfigUtils.isProfileActive(profileOnInstance);
boolean enabled = BuildProfile.from(instance).enabled();
if (enabled) {
LOGGER.debug("Enabling " + instance.target() + " since the profile value matches the active profile.");
} else {
Expand All @@ -72,14 +74,13 @@ void ifBuildProfile(CombinedIndexBuildItem index, BuildProducer<BuildTimeConditi

@BuildStep
void unlessBuildProfile(CombinedIndexBuildItem index, BuildProducer<BuildTimeConditionBuildItem> producer) {
Collection<AnnotationInstance> annotationInstances = index.getIndex().getAnnotations(UNLESS_BUILD_PROFILE);
List<AnnotationInstance> annotationInstances = getAnnotations(index.getIndex(), UNLESS_BUILD_PROFILE);
for (AnnotationInstance instance : annotationInstances) {
String profileOnInstance = instance.value().asString();
boolean enabled = !ConfigUtils.isProfileActive(profileOnInstance);
boolean enabled = BuildProfile.from(instance).disabled();
if (enabled) {
LOGGER.debug("Enabling " + instance.target() + " since the profile value does not match the active profile.");
LOGGER.debug("Enabling " + instance.target() + " since the profile value matches the active profile.");
} else {
LOGGER.debug("Disabling " + instance.target() + " since the profile value matches the active profile.");
LOGGER.debug("Disabling " + instance.target() + " since the profile value does not match the active profile.");
}
producer.produce(new BuildTimeConditionBuildItem(instance.target(), enabled));
}
Expand Down Expand Up @@ -118,17 +119,7 @@ public void accept(AnnotationTarget target, Boolean enabled) {
void buildProperty(DotName annotationName, DotName containingAnnotationName, BiFunction<String, String, Boolean> testFun,
IndexView index, BiConsumer<AnnotationTarget, Boolean> producer) {
Config config = ConfigProviderResolver.instance().getConfig();
List<AnnotationInstance> annotationInstances = new ArrayList<>();
annotationInstances.addAll(index.getAnnotations(annotationName));
// Collect containing annotation instances
// Note that we can't just use the IndexView.getAnnotationsWithRepeatable() method because the containing annotation is not part of the index
for (AnnotationInstance containingInstance : index.getAnnotations(containingAnnotationName)) {
for (AnnotationInstance nestedInstance : containingInstance.value().asNestedArray()) {
// We need to set the target of the containing instance
annotationInstances.add(
AnnotationInstance.create(nestedInstance.name(), containingInstance.target(), nestedInstance.values()));
}
}
List<AnnotationInstance> annotationInstances = getAnnotations(index, annotationName, containingAnnotationName);
for (AnnotationInstance instance : annotationInstances) {
String propertyName = instance.value("name").asString();
String expectedStringValue = instance.value("stringValue").asString();
Expand Down Expand Up @@ -230,7 +221,7 @@ BuildExclusionsBuildItem buildExclusions(List<BuildTimeConditionBuildItem> build
final Map<Kind, Set<String>> map = buildTimeConditions.stream()
.filter(not(BuildTimeConditionBuildItem::isEnabled))
.map(BuildTimeConditionBuildItem::getTarget)
.collect(Collectors.groupingBy(
.collect(groupingBy(
AnnotationTarget::kind,
Collectors.mapping(BuildExclusionsBuildItem::targetMapper, Collectors.toSet())));
return new BuildExclusionsBuildItem(
Expand All @@ -256,4 +247,90 @@ private void transformBean(AnnotationTarget target, TransformationContext ctx, b
transform.done();
}
}

private static List<AnnotationInstance> getAnnotations(IndexView index, DotName annotationName) {
return new ArrayList<>(index.getAnnotations(annotationName));
}

private static List<AnnotationInstance> getAnnotations(
IndexView index,
DotName annotationName,
DotName containingAnnotationName) {

// Single annotation
List<AnnotationInstance> annotationInstances = getAnnotations(index, annotationName);
// Collect containing annotation instances
// Note that we can't just use the IndexView.getAnnotationsWithRepeatable() method because the containing annotation is not part of the index
for (AnnotationInstance containingInstance : index.getAnnotations(containingAnnotationName)) {
for (AnnotationInstance nestedInstance : containingInstance.value().asNestedArray()) {
// We need to set the target of the containing instance
annotationInstances.add(
AnnotationInstance.create(nestedInstance.name(), containingInstance.target(), nestedInstance.values()));
}
}

return annotationInstances;
}

private static class BuildProfile {
private final Set<String> allOf;
private final Set<String> anyOf;

BuildProfile(final Set<String> allOf, final Set<String> anyOf) {
this.allOf = allOf;
this.anyOf = anyOf;
}

boolean allMatch() {
if (allOf.isEmpty()) {
return true;
}

for (String profile : allOf) {
if (!ConfigUtils.isProfileActive(profile)) {
return false;
}
}
return true;
}

boolean anyMatch() {
if (anyOf.isEmpty()) {
return true;
}

for (String profile : anyOf) {
if (ConfigUtils.isProfileActive(profile)) {
return true;
}
}
return false;
}

boolean enabled() {
return allMatch() && anyMatch();
}

boolean disabled() {
return !enabled();
}

private static BuildProfile from(AnnotationInstance instance) {
AnnotationValue value = instance.value();

AnnotationValue allOfValue = instance.value("allOf");
Set<String> allOf = allOfValue != null ? new HashSet<>(asList(allOfValue.asStringArray())) : emptySet();

AnnotationValue anyOfValue = instance.value("anyOf");
Set<String> anyOf = new HashSet<>();
if (value != null) {
anyOf.add(value.asString());
}
if (anyOfValue != null) {
Collections.addAll(anyOf, anyOfValue.asStringArray());
}

return new BuildProfile(allOf, anyOf);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.jboss.jandex.IndexView;

import io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem;
import io.quarkus.arc.processor.Annotations;
import io.quarkus.arc.processor.DotNames;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
Expand All @@ -26,7 +27,8 @@ public class WrongAnnotationUsageProcessor {

@BuildStep
void detect(ArcConfig config, ApplicationIndexBuildItem applicationIndex, CustomScopeAnnotationsBuildItem scopeAnnotations,
TransformedAnnotationsBuildItem transformedAnnotations, BuildProducer<ValidationErrorBuildItem> validationErrors) {
TransformedAnnotationsBuildItem transformedAnnotations, BuildProducer<ValidationErrorBuildItem> validationErrors,
InterceptorResolverBuildItem interceptorResolverBuildItem) {

if (!config.detectWrongAnnotations) {
return;
Expand Down Expand Up @@ -90,6 +92,14 @@ public String apply(AnnotationInstance annotationInstance) {
new IllegalStateException(String.format(
"The %s class %s declares a producer but it must be ignored per the CDI rules",
clazz.nestingType().toString(), clazz.name().toString()))));
} else if (Annotations.containsAny(classAnnotations, interceptorResolverBuildItem.getInterceptorBindings())
|| Annotations.containsAny(clazz.annotations(),
interceptorResolverBuildItem.getInterceptorBindings())) {
// detect interceptor bindings on nested classes
validationErrors.produce(new ValidationErrorBuildItem(
new IllegalStateException(String.format(
"The %s class %s declares an interceptor binding but it must be ignored per CDI rules",
clazz.nestingType().toString(), clazz.name().toString()))));
}
}
}
Expand Down
Loading

0 comments on commit eb397d4

Please sign in to comment.