categoryMap = new HashMap<>(CATEGORIES.length);
diff --git a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/TransactionManagerConfiguration.java b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/TransactionManagerConfiguration.java
index 6719ea8e1d128..1f813fb8d7c5c 100644
--- a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/TransactionManagerConfiguration.java
+++ b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/TransactionManagerConfiguration.java
@@ -72,47 +72,47 @@ public final class TransactionManagerConfiguration {
*/
@ConfigItem
public ObjectStoreConfig objectStore;
-}
-
-@ConfigGroup
-class ObjectStoreConfig {
- /**
- * The name of the directory where the transaction logs will be stored when using the {@code file-system} object store.
- * If the value is not absolute then the directory is relative
- * to the user.dir system property.
- */
- @ConfigItem(defaultValue = "ObjectStore")
- public String directory;
-
- /**
- * The type of object store.
- */
- @ConfigItem(defaultValue = "file-system")
- public ObjectStoreType type;
-
- /**
- * The name of the datasource where the transaction logs will be stored when using the {@code jdbc} object store.
- *
- * If undefined, it will use the default datasource.
- */
- @ConfigItem
- public Optional datasource = Optional.empty();
- /**
- * Whether to create the table if it does not exist.
- */
- @ConfigItem(defaultValue = "false")
- public boolean createTable;
-
- /**
- * Whether to drop the table on startup.
- */
- @ConfigItem(defaultValue = "false")
- public boolean dropTable;
-
- /**
- * The prefix to apply to the table.
- */
- @ConfigItem(defaultValue = "quarkus_")
- public String tablePrefix;
+ @ConfigGroup
+ public static class ObjectStoreConfig {
+ /**
+ * The name of the directory where the transaction logs will be stored when using the {@code file-system} object store.
+ * If the value is not absolute then the directory is relative
+ * to the user.dir system property.
+ */
+ @ConfigItem(defaultValue = "ObjectStore")
+ public String directory;
+
+ /**
+ * The type of object store.
+ */
+ @ConfigItem(defaultValue = "file-system")
+ public ObjectStoreType type;
+
+ /**
+ * The name of the datasource where the transaction logs will be stored when using the {@code jdbc} object store.
+ *
+ * If undefined, it will use the default datasource.
+ */
+ @ConfigItem
+ public Optional datasource = Optional.empty();
+
+ /**
+ * Whether to create the table if it does not exist.
+ */
+ @ConfigItem(defaultValue = "false")
+ public boolean createTable;
+
+ /**
+ * Whether to drop the table on startup.
+ */
+ @ConfigItem(defaultValue = "false")
+ public boolean dropTable;
+
+ /**
+ * The prefix to apply to the table.
+ */
+ @ConfigItem(defaultValue = "quarkus_")
+ public String tablePrefix;
+ }
}
diff --git a/extensions/netty/runtime/src/main/java/io/quarkus/netty/runtime/graal/NettySubstitutions.java b/extensions/netty/runtime/src/main/java/io/quarkus/netty/runtime/graal/NettySubstitutions.java
index 37a79003cd594..ce7cd265223b3 100644
--- a/extensions/netty/runtime/src/main/java/io/quarkus/netty/runtime/graal/NettySubstitutions.java
+++ b/extensions/netty/runtime/src/main/java/io/quarkus/netty/runtime/graal/NettySubstitutions.java
@@ -1,5 +1,6 @@
package io.quarkus.netty.runtime.graal;
+import static io.netty.handler.codec.http.HttpHeaderValues.BR;
import static io.netty.handler.codec.http.HttpHeaderValues.DEFLATE;
import static io.netty.handler.codec.http.HttpHeaderValues.GZIP;
import static io.netty.handler.codec.http.HttpHeaderValues.X_DEFLATE;
@@ -43,13 +44,13 @@
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
-import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.DefaultChannelPromise;
import io.netty.channel.embedded.EmbeddedChannel;
+import io.netty.handler.codec.compression.Brotli;
+import io.netty.handler.codec.compression.BrotliDecoder;
import io.netty.handler.codec.compression.ZlibCodecFactory;
import io.netty.handler.codec.compression.ZlibWrapper;
-import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http2.Http2Exception;
import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
@@ -518,6 +519,10 @@ protected EmbeddedChannel newContentDecoder(String contentEncoding) throws Excep
return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(),
ctx.channel().config(), ZlibCodecFactory.newZlibDecoder(wrapper));
}
+ if (Brotli.isAvailable() && BR.contentEqualsIgnoreCase(contentEncoding)) {
+ return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(),
+ ctx.channel().config(), new BrotliDecoder());
+ }
// 'identity' or unsupported
return null;
@@ -533,21 +538,23 @@ final class Target_io_netty_handler_codec_http2_DelegatingDecompressorFrameListe
@Substitute
protected EmbeddedChannel newContentDecompressor(ChannelHandlerContext ctx, CharSequence contentEncoding)
throws Http2Exception {
- if (!HttpHeaderValues.GZIP.contentEqualsIgnoreCase(contentEncoding)
- && !HttpHeaderValues.X_GZIP.contentEqualsIgnoreCase(contentEncoding)) {
- if (!HttpHeaderValues.DEFLATE.contentEqualsIgnoreCase(contentEncoding)
- && !HttpHeaderValues.X_DEFLATE.contentEqualsIgnoreCase(contentEncoding)) {
- return null;
- } else {
- ZlibWrapper wrapper = this.strict ? ZlibWrapper.ZLIB : ZlibWrapper.ZLIB_OR_NONE;
- return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(),
- ctx.channel().config(),
- new ChannelHandler[] { ZlibCodecFactory.newZlibDecoder(wrapper) });
- }
- } else {
- return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), ctx.channel().config(),
- new ChannelHandler[] { ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP) });
+ if (GZIP.contentEqualsIgnoreCase(contentEncoding) || X_GZIP.contentEqualsIgnoreCase(contentEncoding)) {
+ return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(),
+ ctx.channel().config(), ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP));
}
+ if (DEFLATE.contentEqualsIgnoreCase(contentEncoding) || X_DEFLATE.contentEqualsIgnoreCase(contentEncoding)) {
+ final ZlibWrapper wrapper = strict ? ZlibWrapper.ZLIB : ZlibWrapper.ZLIB_OR_NONE;
+ // To be strict, 'deflate' means ZLIB, but some servers were not implemented correctly.
+ return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(),
+ ctx.channel().config(), ZlibCodecFactory.newZlibDecoder(wrapper));
+ }
+ if (Brotli.isAvailable() && BR.contentEqualsIgnoreCase(contentEncoding)) {
+ return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(),
+ ctx.channel().config(), new BrotliDecoder());
+ }
+
+ // 'identity' or unsupported
+ return null;
}
}
diff --git a/extensions/oidc-client-registration/runtime/banned-signatures.txt b/extensions/oidc-client-registration/runtime/banned-signatures.txt
new file mode 100644
index 0000000000000..be3e7aa782379
--- /dev/null
+++ b/extensions/oidc-client-registration/runtime/banned-signatures.txt
@@ -0,0 +1,2 @@
+@defaultMessage Don't jakarta.json.Json as it is ridiculously slow (see https://github.com/quarkusio/quarkus/issues/42748)
+jakarta.json.Json
diff --git a/extensions/oidc-client-registration/runtime/pom.xml b/extensions/oidc-client-registration/runtime/pom.xml
index 5a7b0038d55bb..94a4d7a812aa2 100644
--- a/extensions/oidc-client-registration/runtime/pom.xml
+++ b/extensions/oidc-client-registration/runtime/pom.xml
@@ -64,6 +64,20 @@
+
+ de.thetaphi
+ forbiddenapis
+
+
+ verify-forbidden-apis
+
+
+ ./banned-signatures.txt
+
+
+
+
+
diff --git a/extensions/oidc-client-registration/runtime/src/main/java/io/quarkus/oidc/client/registration/ClientMetadata.java b/extensions/oidc-client-registration/runtime/src/main/java/io/quarkus/oidc/client/registration/ClientMetadata.java
index 832460f2c0b1b..e1e24a19f0041 100644
--- a/extensions/oidc-client-registration/runtime/src/main/java/io/quarkus/oidc/client/registration/ClientMetadata.java
+++ b/extensions/oidc-client-registration/runtime/src/main/java/io/quarkus/oidc/client/registration/ClientMetadata.java
@@ -1,9 +1,10 @@
package io.quarkus.oidc.client.registration;
+import static io.quarkus.jsonp.JsonProviderHolder.jsonProvider;
+
import java.util.List;
import java.util.Map;
-import jakarta.json.Json;
import jakarta.json.JsonObject;
import jakarta.json.JsonObjectBuilder;
@@ -66,11 +67,11 @@ public static class Builder {
boolean built = false;
Builder() {
- builder = Json.createObjectBuilder();
+ builder = jsonProvider().createObjectBuilder();
}
Builder(JsonObject json) {
- builder = Json.createObjectBuilder(json);
+ builder = jsonProvider().createObjectBuilder(json);
}
public Builder clientName(String clientName) {
@@ -86,7 +87,7 @@ public Builder redirectUri(String redirectUri) {
throw new IllegalStateException();
}
builder.add(OidcConstants.CLIENT_METADATA_REDIRECT_URIS,
- Json.createArrayBuilder().add(redirectUri).build());
+ jsonProvider().createArrayBuilder().add(redirectUri).build());
return this;
}
@@ -95,7 +96,7 @@ public Builder postLogoutUri(String postLogoutUri) {
throw new IllegalStateException();
}
builder.add(OidcConstants.CLIENT_METADATA_POST_LOGOUT_URIS,
- Json.createArrayBuilder().add(postLogoutUri).build());
+ jsonProvider().createArrayBuilder().add(postLogoutUri).build());
return this;
}
@@ -103,7 +104,7 @@ public Builder extraProps(Map extraProps) {
if (built) {
throw new IllegalStateException();
}
- builder.addAll(Json.createObjectBuilder(extraProps));
+ builder.addAll(jsonProvider().createObjectBuilder(extraProps));
return this;
}
diff --git a/extensions/oidc-client-registration/runtime/src/main/java/io/quarkus/oidc/client/registration/runtime/OidcClientRegistrationImpl.java b/extensions/oidc-client-registration/runtime/src/main/java/io/quarkus/oidc/client/registration/runtime/OidcClientRegistrationImpl.java
index be4e72d3e87f3..15048803b4fb6 100644
--- a/extensions/oidc-client-registration/runtime/src/main/java/io/quarkus/oidc/client/registration/runtime/OidcClientRegistrationImpl.java
+++ b/extensions/oidc-client-registration/runtime/src/main/java/io/quarkus/oidc/client/registration/runtime/OidcClientRegistrationImpl.java
@@ -1,5 +1,7 @@
package io.quarkus.oidc.client.registration.runtime;
+import static io.quarkus.jsonp.JsonProviderHolder.jsonProvider;
+
import java.io.IOException;
import java.net.ConnectException;
import java.util.List;
@@ -7,7 +9,6 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
-import jakarta.json.Json;
import jakarta.json.JsonObjectBuilder;
import org.jboss.logging.Logger;
@@ -250,17 +251,17 @@ static class ClientRegistrationHelper {
}
static ClientMetadata createMetadata(Metadata metadata) {
- JsonObjectBuilder json = Json.createObjectBuilder();
+ JsonObjectBuilder json = jsonProvider().createObjectBuilder();
if (metadata.clientName.isPresent()) {
json.add(OidcConstants.CLIENT_METADATA_CLIENT_NAME, metadata.clientName.get());
}
if (metadata.redirectUri.isPresent()) {
json.add(OidcConstants.CLIENT_METADATA_REDIRECT_URIS,
- Json.createArrayBuilder().add(metadata.redirectUri.get()));
+ jsonProvider().createArrayBuilder().add(metadata.redirectUri.get()));
}
if (metadata.postLogoutUri.isPresent()) {
json.add(OidcConstants.POST_LOGOUT_REDIRECT_URI,
- Json.createArrayBuilder().add(metadata.postLogoutUri.get()));
+ jsonProvider().createArrayBuilder().add(metadata.postLogoutUri.get()));
}
for (Map.Entry entry : metadata.extraProps.entrySet()) {
json.add(entry.getKey(), entry.getValue());
diff --git a/extensions/oidc-client-registration/runtime/src/main/java/io/quarkus/oidc/client/registration/runtime/RegisteredClientImpl.java b/extensions/oidc-client-registration/runtime/src/main/java/io/quarkus/oidc/client/registration/runtime/RegisteredClientImpl.java
index 0288735390dc7..9c25b8c12cce3 100644
--- a/extensions/oidc-client-registration/runtime/src/main/java/io/quarkus/oidc/client/registration/runtime/RegisteredClientImpl.java
+++ b/extensions/oidc-client-registration/runtime/src/main/java/io/quarkus/oidc/client/registration/runtime/RegisteredClientImpl.java
@@ -1,12 +1,13 @@
package io.quarkus.oidc.client.registration.runtime;
+import static io.quarkus.jsonp.JsonProviderHolder.jsonProvider;
+
import java.io.IOException;
import java.net.ConnectException;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import jakarta.json.Json;
import jakarta.json.JsonObject;
import jakarta.json.JsonObjectBuilder;
import jakarta.json.JsonValue;
@@ -94,7 +95,7 @@ public Uni update(ClientMetadata newMetadata) {
throw new OidcClientRegistrationException("Client secret can not be modified");
}
- JsonObjectBuilder builder = Json.createObjectBuilder();
+ JsonObjectBuilder builder = jsonProvider().createObjectBuilder();
JsonObject newJsonObject = newMetadata.getJsonObject();
JsonObject currentJsonObject = registeredMetadata.getJsonObject();
diff --git a/extensions/oidc-common/runtime/banned-signatures.txt b/extensions/oidc-common/runtime/banned-signatures.txt
new file mode 100644
index 0000000000000..be3e7aa782379
--- /dev/null
+++ b/extensions/oidc-common/runtime/banned-signatures.txt
@@ -0,0 +1,2 @@
+@defaultMessage Don't jakarta.json.Json as it is ridiculously slow (see https://github.com/quarkusio/quarkus/issues/42748)
+jakarta.json.Json
diff --git a/extensions/oidc-common/runtime/pom.xml b/extensions/oidc-common/runtime/pom.xml
index 4a8e750382909..3600d112cbda2 100644
--- a/extensions/oidc-common/runtime/pom.xml
+++ b/extensions/oidc-common/runtime/pom.xml
@@ -78,6 +78,20 @@
+
+ de.thetaphi
+ forbiddenapis
+
+
+ verify-forbidden-apis
+
+
+ ./banned-signatures.txt
+
+
+
+
+
diff --git a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/AbstractJsonObject.java b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/AbstractJsonObject.java
index 9959b2292f6ad..186f14253993d 100644
--- a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/AbstractJsonObject.java
+++ b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/AbstractJsonObject.java
@@ -1,5 +1,7 @@
package io.quarkus.oidc.common.runtime;
+import static io.quarkus.jsonp.JsonProviderHolder.jsonProvider;
+
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
@@ -7,7 +9,6 @@
import java.util.Map;
import java.util.Set;
-import jakarta.json.Json;
import jakarta.json.JsonArray;
import jakarta.json.JsonNumber;
import jakarta.json.JsonObject;
@@ -20,7 +21,7 @@ public abstract class AbstractJsonObject {
private JsonObject json;
protected AbstractJsonObject() {
- json = Json.createObjectBuilder().build();
+ json = jsonProvider().createObjectBuilder().build();
}
protected AbstractJsonObject(String jsonString) {
@@ -54,7 +55,7 @@ public JsonObject getObject(String name) {
}
public JsonObject getJsonObject() {
- return Json.createObjectBuilder(json).build();
+ return jsonProvider().createObjectBuilder(json).build();
}
public Object get(String name) {
@@ -91,7 +92,7 @@ protected List getListOfStrings(String prop) {
}
public static JsonObject toJsonObject(String json) {
- try (JsonReader jsonReader = Json.createReader(new StringReader(json))) {
+ try (JsonReader jsonReader = jsonProvider().createReader(new StringReader(json))) {
return jsonReader.readObject();
}
}
diff --git a/extensions/oidc/runtime/banned-signatures.txt b/extensions/oidc/runtime/banned-signatures.txt
new file mode 100644
index 0000000000000..be3e7aa782379
--- /dev/null
+++ b/extensions/oidc/runtime/banned-signatures.txt
@@ -0,0 +1,2 @@
+@defaultMessage Don't jakarta.json.Json as it is ridiculously slow (see https://github.com/quarkusio/quarkus/issues/42748)
+jakarta.json.Json
diff --git a/extensions/oidc/runtime/pom.xml b/extensions/oidc/runtime/pom.xml
index 25bb4a266db35..70a6279628b80 100644
--- a/extensions/oidc/runtime/pom.xml
+++ b/extensions/oidc/runtime/pom.xml
@@ -88,6 +88,20 @@
+
+ de.thetaphi
+ forbiddenapis
+
+
+ verify-forbidden-apis
+
+
+ ./banned-signatures.txt
+
+
+
+
+
diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/providers/AzureAccessTokenCustomizer.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/providers/AzureAccessTokenCustomizer.java
index edad83046e7be..0aa8b8e15ea50 100644
--- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/providers/AzureAccessTokenCustomizer.java
+++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/providers/AzureAccessTokenCustomizer.java
@@ -1,11 +1,12 @@
package io.quarkus.oidc.runtime.providers;
+import static io.quarkus.jsonp.JsonProviderHolder.jsonProvider;
+
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Named;
-import jakarta.json.Json;
import jakarta.json.JsonObject;
import io.quarkus.oidc.OIDCException;
@@ -24,7 +25,7 @@ public JsonObject customizeHeaders(JsonObject headers) {
if (nonce != null) {
byte[] nonceSha256 = OidcUtils.getSha256Digest(nonce.getBytes(StandardCharsets.UTF_8));
byte[] newNonceBytes = Base64.getUrlEncoder().withoutPadding().encode(nonceSha256);
- return Json.createObjectBuilder(headers)
+ return jsonProvider().createObjectBuilder(headers)
.add(OidcConstants.NONCE, new String(newNonceBytes, StandardCharsets.UTF_8)).build();
}
return null;
diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleMethodBuildItem.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleMethodBuildItem.java
index 56809719f7b0a..725999bbd0b9a 100644
--- a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleMethodBuildItem.java
+++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleMethodBuildItem.java
@@ -18,15 +18,17 @@ public final class MessageBundleMethodBuildItem extends MultiBuildItem {
private final MethodInfo method;
private final String template;
private final boolean isDefaultBundle;
+ private final boolean hasGeneratedTemplate;
MessageBundleMethodBuildItem(String bundleName, String key, String templateId, MethodInfo method, String template,
- boolean isDefaultBundle) {
+ boolean isDefaultBundle, boolean hasGeneratedTemplate) {
this.bundleName = bundleName;
this.key = key;
this.templateId = templateId;
this.method = method;
this.template = template;
this.isDefaultBundle = isDefaultBundle;
+ this.hasGeneratedTemplate = hasGeneratedTemplate;
}
public String getBundleName() {
@@ -54,6 +56,11 @@ public MethodInfo getMethod() {
return method;
}
+ /**
+ *
+ * @return {@code true} if there is a corresponding method declared on the message bundle interface
+ * @see #getMethod()
+ */
public boolean hasMethod() {
return method != null;
}
@@ -79,6 +86,14 @@ public boolean isDefaultBundle() {
return isDefaultBundle;
}
+ /**
+ *
+ * @return {@code true} if the template was generated, e.g. a message bundle method for an enum
+ */
+ public boolean hasGeneratedTemplate() {
+ return hasGeneratedTemplate;
+ }
+
/**
*
* @return the path
diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleProcessor.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleProcessor.java
index 8b3af1267819e..03783fc656530 100644
--- a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleProcessor.java
+++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleProcessor.java
@@ -701,8 +701,22 @@ void generateExamplePropertiesFiles(List messageBu
List messages = entry.getValue();
messages.sort(Comparator.comparing(MessageBundleMethodBuildItem::getKey));
Path exampleProperties = generatedExamplesDir.resolve(entry.getKey() + ".properties");
- Files.write(exampleProperties,
- messages.stream().map(m -> m.getMethod().name() + "=" + m.getTemplate()).collect(Collectors.toList()));
+ List lines = new ArrayList<>();
+ for (MessageBundleMethodBuildItem m : messages) {
+ if (m.hasMethod()) {
+ if (m.hasGeneratedTemplate()) {
+ // Skip messages with generated templates
+ continue;
+ }
+ // Keys are mapped to method names
+ lines.add(m.getMethod().name() + "=" + m.getTemplate());
+ } else {
+ // No corresponding method declared - use the key instead
+ // For example, there is no method for generated enum constant message keys
+ lines.add(m.getKey() + "=" + m.getTemplate());
+ }
+ }
+ Files.write(exampleProperties, lines);
}
}
@@ -991,6 +1005,7 @@ private String generateImplementation(MessageBundleBuildItem bundle, ClassInfo d
}
keyMap.put(key, new SimpleMessageMethod(method));
+ boolean generatedTemplate = false;
String messageTemplate = messageTemplates.get(method.name());
if (messageTemplate == null) {
messageTemplate = getMessageAnnotationValue(messageAnnotation);
@@ -1042,6 +1057,7 @@ private String generateImplementation(MessageBundleBuildItem bundle, ClassInfo d
}
generatedMessageTemplate.append("{/when}");
messageTemplate = generatedMessageTemplate.toString();
+ generatedTemplate = true;
}
}
}
@@ -1067,7 +1083,7 @@ private String generateImplementation(MessageBundleBuildItem bundle, ClassInfo d
}
MessageBundleMethodBuildItem messageBundleMethod = new MessageBundleMethodBuildItem(bundleName, key, templateId,
- method, messageTemplate, defaultBundleInterface == null);
+ method, messageTemplate, defaultBundleInterface == null, generatedTemplate);
messageTemplateMethods
.produce(messageBundleMethod);
@@ -1138,8 +1154,7 @@ private void generateEnumConstantMessageMethod(ClassCreator bundleCreator, Strin
}
MessageBundleMethodBuildItem messageBundleMethod = new MessageBundleMethodBuildItem(bundleName, enumConstantKey,
- templateId, null, messageTemplate,
- defaultBundleInterface == null);
+ templateId, null, messageTemplate, defaultBundleInterface == null, true);
messageTemplateMethods.produce(messageBundleMethod);
MethodCreator enumConstantMethod = bundleCreator.getMethodCreator(enumConstantKey,
diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteDevModeProcessor.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteDevModeProcessor.java
new file mode 100644
index 0000000000000..4baf3b0756616
--- /dev/null
+++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteDevModeProcessor.java
@@ -0,0 +1,31 @@
+package io.quarkus.qute.deployment;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem;
+import io.quarkus.deployment.IsDevelopment;
+import io.quarkus.deployment.annotations.BuildProducer;
+import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.annotations.BuildSteps;
+import io.quarkus.dev.console.DevConsoleManager;
+import io.quarkus.qute.runtime.devmode.QuteErrorPageSetup;
+
+@BuildSteps(onlyIf = IsDevelopment.class)
+public class QuteDevModeProcessor {
+
+ @BuildStep
+ void collectGeneratedContents(List templatePaths,
+ BuildProducer errors) {
+ Map contents = new HashMap<>();
+ for (TemplatePathBuildItem template : templatePaths) {
+ if (!template.isFileBased()) {
+ contents.put(template.getPath(), template.getContent());
+ }
+ }
+ // Set the global that could be used at runtime when a qute error page is rendered
+ DevConsoleManager.setGlobal(QuteErrorPageSetup.GENERATED_CONTENTS, contents);
+ }
+
+}
diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/i18n/MessageBundleEnumExampleFileTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/i18n/MessageBundleEnumExampleFileTest.java
new file mode 100644
index 0000000000000..008a289fa6340
--- /dev/null
+++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/i18n/MessageBundleEnumExampleFileTest.java
@@ -0,0 +1,64 @@
+package io.quarkus.qute.deployment.i18n;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Properties;
+
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.qute.i18n.Message;
+import io.quarkus.qute.i18n.MessageBundle;
+import io.quarkus.test.ProdBuildResults;
+import io.quarkus.test.ProdModeTestResults;
+import io.quarkus.test.QuarkusProdModeTest;
+
+public class MessageBundleEnumExampleFileTest {
+
+ @RegisterExtension
+ static final QuarkusProdModeTest config = new QuarkusProdModeTest()
+ .withApplicationRoot(root -> root
+ .addClasses(Messages.class, MyEnum.class)
+ .addAsResource(new StringAsset("""
+ myEnum_ON=On
+ myEnum_OFF=Off
+ myEnum_UNDEFINED=Undefined
+ """),
+ "messages/enu.properties"));
+
+ @ProdBuildResults
+ ProdModeTestResults testResults;
+
+ @Test
+ public void testExampleProperties() throws FileNotFoundException, IOException {
+ Path path = testResults.getBuildDir().resolve("qute-i18n-examples").resolve("enu.properties");
+ assertTrue(path.toFile().canRead());
+ Properties props = new Properties();
+ props.load(new FileInputStream(path.toFile()));
+ assertEquals(3, props.size());
+ assertTrue(props.containsKey("myEnum_ON"));
+ assertTrue(props.containsKey("myEnum_OFF"));
+ assertTrue(props.containsKey("myEnum_UNDEFINED"));
+ }
+
+ @MessageBundle(value = "enu", locale = "en")
+ public interface Messages {
+
+ // Replaced with:
+ // @Message("{#when myEnum}"
+ // + "{#is ON}{enu:myEnum_ON}"
+ // + "{#is OFF}{enu:myEnum_OFF}"
+ // + "{#is UNDEFINED}{enu:myEnum_UNDEFINED}"
+ // + "{/when}")
+ @Message
+ String myEnum(MyEnum myEnum);
+
+ }
+
+}
diff --git a/extensions/qute/runtime/src/main/java/io/quarkus/qute/i18n/Message.java b/extensions/qute/runtime/src/main/java/io/quarkus/qute/i18n/Message.java
index 93c5fbe6b1327..b8b8a43ae5955 100644
--- a/extensions/qute/runtime/src/main/java/io/quarkus/qute/i18n/Message.java
+++ b/extensions/qute/runtime/src/main/java/io/quarkus/qute/i18n/Message.java
@@ -27,8 +27,7 @@
* There is a convenient way to localize enums.
*
* If there is a message bundle method that accepts a single parameter of an enum type and has no message template defined then
- * it
- * receives a generated template:
+ * it receives a generated template:
*
*
* {#when enumParamName}
diff --git a/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/devmode/QuteErrorPageSetup.java b/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/devmode/QuteErrorPageSetup.java
index b7ced362defac..916522c98443f 100644
--- a/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/devmode/QuteErrorPageSetup.java
+++ b/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/devmode/QuteErrorPageSetup.java
@@ -2,6 +2,7 @@
import java.io.BufferedReader;
import java.io.IOException;
+import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
@@ -12,6 +13,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.ListIterator;
+import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.stream.Collectors;
@@ -19,6 +21,7 @@
import org.jboss.logging.Logger;
import io.quarkus.dev.ErrorPageGenerators;
+import io.quarkus.dev.console.DevConsoleManager;
import io.quarkus.dev.spi.HotReplacementContext;
import io.quarkus.dev.spi.HotReplacementSetup;
import io.quarkus.qute.Engine;
@@ -33,6 +36,8 @@ public class QuteErrorPageSetup implements HotReplacementSetup {
private static final Logger LOG = Logger.getLogger(QuteErrorPageSetup.class);
+ public static final String GENERATED_CONTENTS = "io.quarkus.qute.generatedContents";
+
private static final String TEMPLATE_EXCEPTION = "io.quarkus.qute.TemplateException";
private static final String ORIGIN = "io.quarkus.qute.TemplateNode$Origin";
@@ -139,6 +144,10 @@ String getProblemInfo(int index, Throwable problem, Template problemTemplate, Es
LOG.warn("Unable to read the template source: " + templateId, e);
}
+ if (sourceLines.isEmpty()) {
+ return Arrays.stream(messageLines).collect(Collectors.joining("
"));
+ }
+
List realLines = new ArrayList<>();
boolean endLinesSkipped = false;
if (sourceLines.size() > 15) {
@@ -187,6 +196,14 @@ private BufferedReader getBufferedReader(String templateId) throws IOException {
}
}
}
+ // Source file not available - try to search the generated contents
+ Map generatedContents = DevConsoleManager.getGlobal(GENERATED_CONTENTS);
+ if (generatedContents != null) {
+ String template = generatedContents.get(templateId);
+ if (template != null) {
+ return new BufferedReader(new StringReader(template));
+ }
+ }
throw new IllegalStateException("Template source not available");
}
diff --git a/extensions/resteasy-reactive/rest-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java b/extensions/resteasy-reactive/rest-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java
index 9d154c483eb96..c1fa069d6e2cc 100644
--- a/extensions/resteasy-reactive/rest-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java
+++ b/extensions/resteasy-reactive/rest-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java
@@ -27,7 +27,6 @@
import org.jboss.jandex.Indexer;
import org.jboss.jandex.MethodInfo;
import org.jboss.logging.Logger;
-import org.jboss.resteasy.reactive.common.jaxrs.RuntimeDelegateImpl;
import org.jboss.resteasy.reactive.common.model.InterceptorContainer;
import org.jboss.resteasy.reactive.common.model.PreMatchInterceptorContainer;
import org.jboss.resteasy.reactive.common.model.ResourceInterceptor;
@@ -340,8 +339,7 @@ public void setupEndpoints(BeanArchiveIndexBuildItem beanArchiveIndexBuildItem,
@BuildStep
void registerRuntimeDelegateImpl(BuildProducer serviceProviders) {
- serviceProviders.produce(new ServiceProviderBuildItem(RuntimeDelegate.class.getName(),
- RuntimeDelegateImpl.class.getName()));
+ serviceProviders.produce(ServiceProviderBuildItem.allProvidersFromClassPath(RuntimeDelegate.class.getName()));
}
/*
diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/JacksonDeserializerFactory.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/JacksonDeserializerFactory.java
index fa1fdb002bf0a..ebc47ab056de6 100644
--- a/extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/JacksonDeserializerFactory.java
+++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/JacksonDeserializerFactory.java
@@ -239,18 +239,18 @@ private boolean deserializeObject(ClassInfo classInfo, ResultHandle objHandle, C
ResultHandle nextField = loopCreator
.invokeInterfaceMethod(ofMethod(Iterator.class, "next", Object.class), fieldsIterator);
ResultHandle mapEntry = loopCreator.checkCast(nextField, Map.Entry.class);
- ResultHandle fieldName = loopCreator
- .invokeInterfaceMethod(ofMethod(Map.Entry.class, "getKey", Object.class), mapEntry);
ResultHandle fieldValue = loopCreator.checkCast(loopCreator
.invokeInterfaceMethod(ofMethod(Map.Entry.class, "getValue", Object.class), mapEntry), JsonNode.class);
- loopCreator.ifTrue(loopCreator.invokeVirtualMethod(ofMethod(JsonNode.class, "isNull", boolean.class), fieldValue))
- .trueBranch().continueScope(loopCreator);
+ BytecodeCreator fieldReader = loopCreator
+ .ifTrue(loopCreator.invokeVirtualMethod(ofMethod(JsonNode.class, "isNull", boolean.class), fieldValue))
+ .falseBranch();
+
+ ResultHandle fieldName = fieldReader
+ .invokeInterfaceMethod(ofMethod(Map.Entry.class, "getKey", Object.class), mapEntry);
+ Switch.StringSwitch strSwitch = fieldReader.stringSwitch(fieldName);
- Set deserializedFields = new HashSet<>();
- ResultHandle deserializationContext = deserialize.getMethodParam(1);
- Switch.StringSwitch strSwitch = loopCreator.stringSwitch(fieldName);
- return deserializeFields(classCreator, classInfo, deserializationContext, objHandle, fieldValue, deserializedFields,
+ return deserializeFields(classCreator, classInfo, deserialize.getMethodParam(1), objHandle, fieldValue, new HashSet<>(),
strSwitch, parseTypeParameters(classInfo, classCreator));
}
diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/MapWrapper.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/MapWrapper.java
new file mode 100644
index 0000000000000..6bc5bda55d642
--- /dev/null
+++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/MapWrapper.java
@@ -0,0 +1,32 @@
+package io.quarkus.resteasy.reactive.jackson.deployment.test;
+
+import java.util.Map;
+
+public class MapWrapper {
+
+ private String name;
+ private Map properties;
+
+ public MapWrapper() {
+ }
+
+ public MapWrapper(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Map getProperties() {
+ return properties;
+ }
+
+ public void setProperties(Map properties) {
+ this.properties = properties;
+ }
+}
diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonResource.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonResource.java
index 855c43625c09e..861f01ce08a96 100644
--- a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonResource.java
+++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonResource.java
@@ -123,6 +123,13 @@ public StateRecord echoDog(StateRecord stateRecord) {
return stateRecord;
}
+ @POST
+ @Path("/null-map-echo")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public MapWrapper echoNullMap(MapWrapper mapWrapper) {
+ return mapWrapper;
+ }
+
@EnableSecureSerialization
@GET
@Path("/abstract-cat")
diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonTest.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonTest.java
index d2f22569f9a7a..a5fa4d498c923 100644
--- a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonTest.java
+++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonTest.java
@@ -36,7 +36,7 @@ public JavaArchive get() {
AbstractPet.class, Dog.class, Cat.class, Veterinarian.class, AbstractNamedPet.class,
AbstractUnsecuredPet.class, UnsecuredPet.class, SecuredPersonInterface.class, Frog.class,
Pond.class, FrogBodyParts.class, FrogBodyParts.BodyPart.class, ContainerDTO.class,
- NestedInterface.class, StateRecord.class)
+ NestedInterface.class, StateRecord.class, MapWrapper.class)
.addAsResource(new StringAsset("admin-expression=admin\n" +
"user-expression=user\n" +
"birth-date-roles=alice,bob\n"), "application.properties");
@@ -733,4 +733,18 @@ public void testRecordEcho() {
assertTrue(first >= 0);
assertEquals(first, last);
}
+
+ @Test
+ public void testNullMapEcho() {
+ RestAssured
+ .with()
+ .body(new MapWrapper("test"))
+ .contentType("application/json; charset=utf-8")
+ .post("/simple/null-map-echo")
+ .then()
+ .statusCode(200)
+ .contentType("application/json")
+ .body("name", Matchers.is("test"))
+ .body("properties", Matchers.nullValue());
+ }
}
diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonWithReflectionFreeSerializersTest.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonWithReflectionFreeSerializersTest.java
index 65dec05aa59a4..10ea3d373ce91 100644
--- a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonWithReflectionFreeSerializersTest.java
+++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonWithReflectionFreeSerializersTest.java
@@ -25,7 +25,7 @@ public JavaArchive get() {
AbstractPet.class, Dog.class, Cat.class, Veterinarian.class, AbstractNamedPet.class,
AbstractUnsecuredPet.class, UnsecuredPet.class, SecuredPersonInterface.class, Frog.class,
Pond.class, FrogBodyParts.class, FrogBodyParts.BodyPart.class, ContainerDTO.class,
- NestedInterface.class, StateRecord.class)
+ NestedInterface.class, StateRecord.class, MapWrapper.class)
.addAsResource(new StringAsset("admin-expression=admin\n" +
"user-expression=user\n" +
"birth-date-roles=alice,bob\n" +
diff --git a/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/DelayedExecutionInvoker.java b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/DelayedExecutionInvoker.java
index 6e343ac35ab5e..4faa121b25794 100644
--- a/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/DelayedExecutionInvoker.java
+++ b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/DelayedExecutionInvoker.java
@@ -53,12 +53,7 @@ public CompletionStage invoke(ScheduledExecution execution) throws Excepti
executor.schedule(new Runnable() {
@Override
public void run() {
- try {
- delegate.invoke(execution);
- ret.complete(null);
- } catch (Exception e) {
- ret.completeExceptionally(e);
- }
+ invokeComplete(ret, execution);
}
}, delay, TimeUnit.MILLISECONDS);
return ret;
diff --git a/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/DelegateInvoker.java b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/DelegateInvoker.java
index a2245862d7fb0..5b25e3bebd99b 100644
--- a/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/DelegateInvoker.java
+++ b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/DelegateInvoker.java
@@ -2,6 +2,7 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
+import java.util.function.BiConsumer;
import io.quarkus.scheduler.ScheduledExecution;
@@ -30,4 +31,17 @@ protected CompletionStage invokeDelegate(ScheduledExecution execution) {
return CompletableFuture.failedStage(e);
}
}
+
+ protected void invokeComplete(CompletableFuture ret, ScheduledExecution execution) {
+ invokeDelegate(execution).whenComplete(new BiConsumer<>() {
+ @Override
+ public void accept(Void r, Throwable t) {
+ if (t != null) {
+ ret.completeExceptionally(t);
+ } else {
+ ret.complete(null);
+ }
+ }
+ });
+ }
}
diff --git a/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/OffloadingInvoker.java b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/OffloadingInvoker.java
index 3026c382fd282..23b8605aa6e5a 100644
--- a/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/OffloadingInvoker.java
+++ b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/OffloadingInvoker.java
@@ -1,6 +1,7 @@
package io.quarkus.scheduler.common.runtime;
import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import io.quarkus.scheduler.ScheduledExecution;
@@ -28,6 +29,7 @@ public OffloadingInvoker(ScheduledInvoker delegate, Vertx vertx) {
@Override
public CompletionStage invoke(ScheduledExecution execution) throws Exception {
+ CompletableFuture ret = new CompletableFuture<>();
Context context = VertxContext.getOrCreateDuplicatedContext(vertx);
VertxContextSafetyToggle.setContextSafe(context, true);
if (delegate.isBlocking()) {
@@ -40,7 +42,7 @@ public void handle(Void event) {
VirtualThreadsRecorder.getCurrent().execute(new Runnable() {
@Override
public void run() {
- doInvoke(execution);
+ invokeComplete(ret, execution);
}
});
}
@@ -49,7 +51,7 @@ public void run() {
context.executeBlocking(new Callable() {
@Override
public Void call() {
- doInvoke(execution);
+ invokeComplete(ret, execution);
return null;
}
}, false);
@@ -58,19 +60,11 @@ public Void call() {
context.runOnContext(new Handler() {
@Override
public void handle(Void event) {
- doInvoke(execution);
+ invokeComplete(ret, execution);
}
});
}
- return null;
- }
-
- void doInvoke(ScheduledExecution execution) {
- try {
- delegate.invoke(execution);
- } catch (Throwable t) {
- // already logged by the StatusEmitterInvoker
- }
+ return ret;
}
}
diff --git a/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/ScheduledInvoker.java b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/ScheduledInvoker.java
index a7f1f6a80e702..b57f91648ddfe 100644
--- a/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/ScheduledInvoker.java
+++ b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/ScheduledInvoker.java
@@ -11,7 +11,7 @@ public interface ScheduledInvoker {
/**
* @param execution
- * @return the result
+ * @return the result, never {@code null}
* @throws Exception
*/
CompletionStage invoke(ScheduledExecution execution) throws Exception;
diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java
index 6d839c1277323..1a1355759037d 100644
--- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java
+++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java
@@ -1100,7 +1100,7 @@ private void implementCreateForSyntheticBean(ClassCreator beanCreator, BeanInfo
List matchingIPs = new ArrayList<>();
for (InjectionPointInfo injectionPoint : bean.getDeployment().getInjectionPoints()) {
- if (bean.equals(injectionPoint.getResolvedBean())) {
+ if (!injectionPoint.isSynthetic() && bean.equals(injectionPoint.getResolvedBean())) {
matchingIPs.add(injectionPoint);
}
}
diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformImportsImpl.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformImportsImpl.java
index 1b747cb158390..b332eb3e72bff 100644
--- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformImportsImpl.java
+++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/PlatformImportsImpl.java
@@ -81,8 +81,10 @@ public void addPlatformDescriptor(String groupId, String artifactId, String clas
artifactId.substring(0,
artifactId.length() - BootstrapConstants.PLATFORM_DESCRIPTOR_ARTIFACT_ID_SUFFIX.length()),
version);
- platformImports.computeIfAbsent(bomCoords, c -> new PlatformImport()).descriptorFound = true;
- platformBoms.add(bomCoords);
+ platformImports.computeIfAbsent(bomCoords, c -> {
+ platformBoms.add(bomCoords);
+ return new PlatformImport();
+ }).descriptorFound = true;
}
public void addPlatformProperties(String groupId, String artifactId, String classifier, String type, String version,
@@ -92,21 +94,24 @@ public void addPlatformProperties(String groupId, String artifactId, String clas
artifactId.length() - BootstrapConstants.PLATFORM_PROPERTIES_ARTIFACT_ID_SUFFIX.length()),
version);
platformImports.computeIfAbsent(bomCoords, c -> new PlatformImport());
- importedPlatformBoms.computeIfAbsent(groupId, g -> new ArrayList<>()).add(bomCoords);
-
- final Properties props = new Properties();
- try (InputStream is = Files.newInputStream(propsPath)) {
- props.load(is);
- } catch (IOException e) {
- throw new AppModelResolverException("Failed to read properties from " + propsPath, e);
- }
- for (Map.Entry, ?> prop : props.entrySet()) {
- final String name = String.valueOf(prop.getKey());
- if (name.startsWith(BootstrapConstants.PLATFORM_PROPERTY_PREFIX)) {
- if (isPlatformReleaseInfo(name)) {
- addPlatformRelease(name, String.valueOf(prop.getValue()));
- } else {
- collectedProps.putIfAbsent(name, String.valueOf(prop.getValue().toString()));
+ importedPlatformBoms.computeIfAbsent(groupId, g -> new ArrayList<>());
+ if (!importedPlatformBoms.get(groupId).contains(bomCoords)) {
+ importedPlatformBoms.get(groupId).add(bomCoords);
+
+ final Properties props = new Properties();
+ try (InputStream is = Files.newInputStream(propsPath)) {
+ props.load(is);
+ } catch (IOException e) {
+ throw new AppModelResolverException("Failed to read properties from " + propsPath, e);
+ }
+ for (Map.Entry, ?> prop : props.entrySet()) {
+ final String name = String.valueOf(prop.getKey());
+ if (name.startsWith(BootstrapConstants.PLATFORM_PROPERTY_PREFIX)) {
+ if (isPlatformReleaseInfo(name)) {
+ addPlatformRelease(name, String.valueOf(prop.getValue()));
+ } else {
+ collectedProps.putIfAbsent(name, String.valueOf(prop.getValue().toString()));
+ }
}
}
}
diff --git a/independent-projects/bootstrap/app-model/src/test/java/io/quarkus/bootstrap/model/PlatformImportsTest.java b/independent-projects/bootstrap/app-model/src/test/java/io/quarkus/bootstrap/model/PlatformImportsTest.java
index 56106751314b2..1a0e70abcbe7d 100644
--- a/independent-projects/bootstrap/app-model/src/test/java/io/quarkus/bootstrap/model/PlatformImportsTest.java
+++ b/independent-projects/bootstrap/app-model/src/test/java/io/quarkus/bootstrap/model/PlatformImportsTest.java
@@ -114,6 +114,14 @@ public void multiplePlatformReleaseInTheSameStream() throws Exception {
GACTV.fromString("io.playground:acme-bom::pom:2.2.2")))));
}
+ @Test
+ public void duplicatePlatformDescriptorsAreIgnored() {
+ final PlatformImportsImpl pi = new PlatformImportsImpl();
+ pi.addPlatformDescriptor("io.playground", "acme-bom-quarkus-platform-descriptor", "", "", "1.1");
+ pi.addPlatformDescriptor("io.playground", "acme-bom-quarkus-platform-descriptor", "", "", "1.1");
+ assertEquals(1, pi.getImportedPlatformBoms().size());
+ }
+
private PlatformProps newPlatformProps() throws IOException {
final PlatformProps p = new PlatformProps();
platformProps.add(p);
diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/CuratedApplication.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/CuratedApplication.java
index 852e3c9147f89..2a9a7f0462fb5 100644
--- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/CuratedApplication.java
+++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/CuratedApplication.java
@@ -356,9 +356,7 @@ public QuarkusClassLoader createDeploymentClassLoader() {
if (configuredClassLoading.isRemovedArtifact(dependency.getKey())) {
continue;
}
- if (dependency.isRuntimeCp() && dependency.isJar() &&
- (dependency.isReloadable() && appModel.getReloadableWorkspaceDependencies().contains(dependency.getKey()) ||
- configuredClassLoading.isReloadableArtifact(dependency.getKey()))) {
+ if (isReloadableRuntimeDependency(dependency)) {
processCpElement(dependency, element -> addCpElement(builder, dependency, element));
}
}
@@ -368,6 +366,12 @@ public QuarkusClassLoader createDeploymentClassLoader() {
return builder.build();
}
+ private boolean isReloadableRuntimeDependency(ResolvedDependency dependency) {
+ return dependency.isRuntimeCp() && dependency.isJar() &&
+ (dependency.isReloadable() && appModel.getReloadableWorkspaceDependencies().contains(dependency.getKey()) ||
+ configuredClassLoading.isReloadableArtifact(dependency.getKey()));
+ }
+
public String getClassLoaderNameSuffix() {
return quarkusBootstrap.getBaseName() != null ? " for " + quarkusBootstrap.getBaseName() : "";
}
@@ -405,9 +409,7 @@ public QuarkusClassLoader createRuntimeClassLoader(ClassLoader base, Map addCpElement(builder, dependency, element));
}
}
diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java
index 9368790bf7509..7f8ff90de6705 100644
--- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java
+++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java
@@ -625,22 +625,77 @@ public List getElementsWithResource(String name) {
public List getElementsWithResource(String name, boolean localOnly) {
ensureOpen(name);
- boolean parentFirst = parentFirst(name, getClassPathResourceIndex());
+ final boolean parentFirst = parentFirst(name, getClassPathResourceIndex());
- List ret = new ArrayList<>();
+ List result = List.of();
- if (parentFirst && !localOnly && parent instanceof QuarkusClassLoader) {
- ret.addAll(((QuarkusClassLoader) parent).getElementsWithResource(name));
+ if (parentFirst && !localOnly && parent instanceof QuarkusClassLoader parentQcl) {
+ result = parentQcl.getElementsWithResource(name);
}
- List classPathElements = getClassPathResourceIndex().getClassPathElements(name);
- ret.addAll(classPathElements);
+ result = joinAndDedupe(result, getClassPathResourceIndex().getClassPathElements(name));
- if (!parentFirst && !localOnly && parent instanceof QuarkusClassLoader) {
- ret.addAll(((QuarkusClassLoader) parent).getElementsWithResource(name));
+ if (!parentFirst && !localOnly && parent instanceof QuarkusClassLoader parentQcl) {
+ result = joinAndDedupe(result, parentQcl.getElementsWithResource(name));
}
- return ret;
+ return result;
+ }
+
+ /**
+ * Returns a list containing elements from two lists eliminating duplicates. Elements from the first list
+ * will appear in the result before elements from the second list.
+ *
+ * The current implementation assumes that none of the lists contains duplicates on their own but some elements
+ * may be present in both lists.
+ *
+ * @param list1 first list
+ * @param list2 second list
+ * @return resulting list
+ */
+ private static List joinAndDedupe(List list1, List list2) {
+ // it appears, in the vast majority of cases at least one of the lists will be empty
+ if (list1.isEmpty()) {
+ return list2;
+ }
+ if (list2.isEmpty()) {
+ return list1;
+ }
+ final List result = new ArrayList<>(list1.size() + list2.size());
+ // it looks like in most cases at this point list1 (representing elements from the parent cl) will contain only one element
+ if (list1.size() == 1) {
+ final T firstCpe = list1.get(0);
+ result.add(firstCpe);
+ for (var cpe : list2) {
+ if (cpe != firstCpe) {
+ result.add(cpe);
+ }
+ }
+ return result;
+ }
+ result.addAll(list1);
+ for (var cpe : list2) {
+ if (!containsReference(list1, cpe)) {
+ result.add(cpe);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Checks whether a list contains an element that references the other argument.
+ *
+ * @param list list of elements
+ * @param e element to look for
+ * @return true if the list contains an element referencing {@code e}, otherwise - false
+ */
+ private static boolean containsReference(List list, T e) {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ if (e == list.get(i)) {
+ return true;
+ }
+ }
+ return false;
}
public Set getReloadableClassNames() {
@@ -902,6 +957,10 @@ public QuarkusClassLoader build() {
return new QuarkusClassLoader(this);
}
+ @Override
+ public String toString() {
+ return "QuarkusClassLoader.Builder:" + name + "@" + Integer.toHexString(hashCode());
+ }
}
public ClassLoader parent() {
diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/jbang/JBangDevModeLauncherImpl.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/jbang/JBangDevModeLauncherImpl.java
index 207281a09cb35..49dbca6aee629 100644
--- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/jbang/JBangDevModeLauncherImpl.java
+++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/jbang/JBangDevModeLauncherImpl.java
@@ -15,6 +15,7 @@
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
+import java.util.Properties;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -85,7 +86,7 @@ public static Closeable main(String... args) {
Path srcDir = projectRoot.resolve("src/main/java");
Files.createDirectories(srcDir);
- Files.createSymbolicLink(srcDir.resolve(sourceFile.getFileName().toString()), sourceFile);
+ Path source = Files.createSymbolicLink(srcDir.resolve(sourceFile.getFileName().toString()), sourceFile);
final LocalProject currentProject = LocalProject.loadWorkspace(projectRoot);
final ResolvedDependency appArtifact = ResolvedDependencyBuilder.newInstance()
.setCoords(currentProject.getAppArtifact(ArtifactCoords.TYPE_JAR))
@@ -93,6 +94,8 @@ public static Closeable main(String... args) {
.setWorkspaceModule(currentProject.toWorkspaceModule())
.build();
+ Properties configurationProperties = getConfigurationProperties(source);
+
//todo : proper support for everything
final QuarkusBootstrap.Builder builder = QuarkusBootstrap.builder()
.setBaseClassLoader(JBangDevModeLauncherImpl.class.getClassLoader())
@@ -117,7 +120,9 @@ public static Closeable main(String... args) {
return artifact;
}).collect(Collectors.toList()))
.setApplicationRoot(targetClasses)
- .setProjectRoot(projectRoot);
+ .setProjectRoot(projectRoot)
+ .setBuildSystemProperties(configurationProperties)
+ .setRuntimeProperties(configurationProperties);
Map context = new HashMap<>();
context.put("app-project", currentProject);
@@ -174,4 +179,19 @@ private static String getQuarkusVersion() {
throw new RuntimeException(e);
}
}
+
+ private static Properties getConfigurationProperties(final Path source) throws IOException {
+ Properties properties = new Properties();
+ for (String line : Files.readAllLines(source)) {
+ if (line.startsWith("//Q:CONFIG")) {
+ String conf = line.substring(10).trim();
+ int equals = conf.indexOf("=");
+ if (equals == -1) {
+ throw new RuntimeException("invalid config " + line);
+ }
+ properties.setProperty(conf.substring(0, equals), conf.substring(equals + 1));
+ }
+ }
+ return properties;
+ }
}
diff --git a/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/application/build.gradle.kts b/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/application/build.gradle.kts
new file mode 100644
index 0000000000000..8a97977aab3f8
--- /dev/null
+++ b/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/application/build.gradle.kts
@@ -0,0 +1,17 @@
+plugins {
+ id("io.quarkus")
+}
+
+dependencies {
+ implementation(project(":module1"))
+
+ implementation("io.quarkus:quarkus-rest-jackson")
+ implementation("io.quarkus:quarkus-rest")
+ implementation("io.quarkus:quarkus-kotlin")
+ implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
+ implementation("io.quarkus:quarkus-arc")
+ implementation("io.quarkus:quarkus-grpc")
+
+ testImplementation("io.quarkus:quarkus-junit5")
+ testImplementation("io.rest-assured:rest-assured")
+}
diff --git a/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/application/src/main/kotlin/org/acme/GreetingResource.kt b/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/application/src/main/kotlin/org/acme/GreetingResource.kt
new file mode 100644
index 0000000000000..3fe47cc77e17a
--- /dev/null
+++ b/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/application/src/main/kotlin/org/acme/GreetingResource.kt
@@ -0,0 +1,23 @@
+package org.acme
+
+import io.quarkus.grpc.GrpcClient
+import jakarta.ws.rs.GET
+import jakarta.ws.rs.Path
+import jakarta.ws.rs.Produces
+import jakarta.ws.rs.core.MediaType
+import org.acme.module1.SomeClass1
+import org.acme.proto.Greeter
+
+@Path("/version")
+class GreetingResource {
+
+ @GrpcClient
+ lateinit var hello: Greeter
+
+ @GET
+ @Produces(MediaType.TEXT_PLAIN)
+ fun getVersion(): String {
+ return SomeClass1().getVersion()
+ }
+}
+
diff --git a/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/application/src/main/resources/META-INF/resources/index.html b/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/application/src/main/resources/META-INF/resources/index.html
new file mode 100644
index 0000000000000..c2ccecac788cf
--- /dev/null
+++ b/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/application/src/main/resources/META-INF/resources/index.html
@@ -0,0 +1,285 @@
+
+
+
+
+ code-with-quarkus - 1.0.0-SNAPSHOT
+
+
+
+
+
+
+
+
+
Congratulations!
+
+
Application
+
+ - GroupId:
org.acme
+ - ArtifactId:
code-with-quarkus
+ - Version:
1.0.0-SNAPSHOT
+ - Quarkus Version:
3.7.4
+
+
+
+
+
+
+
+
+
+
You just made a Quarkus application.
+
This page is served by Quarkus.
+
Visit the Dev UI
+
This page: src/main/resources/META-INF/resources/index.html
+
App configuration: src/main/resources/application.properties
+
Static assets: src/main/resources/META-INF/resources/
+
Code: src/main/kotlin
+
Generated starter code:
+
+ -
+ RESTEasy Reactive Easily start your Reactive RESTful Web Services
+
› @Path: /hello
+
› Related guide
+
+
+
+
+
+
Selected extensions
+
+ - RESTEasy Reactive Jackson
+ - RESTEasy Reactive (guide)
+ - Kotlin (guide)
+
+
+
Practical step-by-step guides to help you achieve a specific goal. Use them to help get your work
+ done.
+
+
Everyone has a favorite IDE they like to use to code. Learn how to configure yours to maximize your
+ Quarkus productivity.
+
+
+
+
+
diff --git a/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/application/src/main/resources/application.properties b/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/application/src/main/resources/application.properties
new file mode 100644
index 0000000000000..dd47fa0de94ee
--- /dev/null
+++ b/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/application/src/main/resources/application.properties
@@ -0,0 +1 @@
+quarkus.generate-code.grpc.scan-for-proto=org.acme:module1
\ No newline at end of file
diff --git a/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/build.gradle.kts b/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/build.gradle.kts
new file mode 100644
index 0000000000000..8a0bba94937f6
--- /dev/null
+++ b/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/build.gradle.kts
@@ -0,0 +1,43 @@
+plugins {
+ kotlin("jvm") version "2.0.21"
+ kotlin("plugin.allopen") version "2.0.21"
+
+ id("io.quarkus") apply false
+}
+allprojects {
+
+ repositories {
+ mavenLocal {
+ content {
+ includeGroupByRegex("io.quarkus.*")
+ includeGroup("org.hibernate.orm")
+ }
+ }
+ mavenCentral()
+ }
+
+}
+subprojects {
+ apply(plugin = "org.jetbrains.kotlin.jvm")
+ apply(plugin = "org.jetbrains.kotlin.plugin.allopen")
+
+
+ tasks.withType {
+ systemProperty("java.util.logging.manager", "org.jboss.logmanager.LogManager")
+ }
+
+ val quarkusPlatformGroupId: String by project
+ val quarkusPlatformArtifactId: String by project
+ val quarkusPlatformVersion: String by project
+
+ dependencies {
+ implementation(enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}"))
+ }
+
+ allOpen {
+ annotation("jakarta.ws.rs.Path")
+ annotation("jakarta.enterprise.context.ApplicationScoped")
+ annotation("jakarta.persistence.Entity")
+ annotation("io.quarkus.test.junit.QuarkusTest")
+ }
+}
diff --git a/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/gradle.properties b/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/gradle.properties
new file mode 100644
index 0000000000000..c1ec5e0323249
--- /dev/null
+++ b/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/gradle.properties
@@ -0,0 +1,5 @@
+quarkusPlatformArtifactId=quarkus-bom
+quarkusPlatformGroupId=io.quarkus
+
+group=org.acme
+version=1.0.0-SNAPSHOT
diff --git a/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/module1/build.gradle.kts b/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/module1/build.gradle.kts
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/module1/src/main/kotlin/org/acme/module1/SomeClass1.kt b/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/module1/src/main/kotlin/org/acme/module1/SomeClass1.kt
new file mode 100644
index 0000000000000..5bb8560fce908
--- /dev/null
+++ b/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/module1/src/main/kotlin/org/acme/module1/SomeClass1.kt
@@ -0,0 +1,7 @@
+package org.acme.module1
+
+class SomeClass1 {
+ fun getVersion(): String {
+ return "123"
+ }
+}
\ No newline at end of file
diff --git a/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/module1/src/main/resources/proto/module1.proto b/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/module1/src/main/resources/proto/module1.proto
new file mode 100644
index 0000000000000..0378c5b7844a8
--- /dev/null
+++ b/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/module1/src/main/resources/proto/module1.proto
@@ -0,0 +1,23 @@
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "org.acme.proto";
+option java_outer_classname = "HelloWorldProto";
+
+package helloworld;
+
+// The greeting service definition.
+service Greeter {
+ // Sends a greeting
+ rpc SayHello (HelloRequest) returns (HelloReply) {}
+}
+
+// The request message containing the user's name.
+message HelloRequest {
+ string name = 1;
+}
+
+// The response message containing the greetings
+message HelloReply {
+ string message = 1;
+}
\ No newline at end of file
diff --git a/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/settings.gradle b/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/settings.gradle
new file mode 100644
index 0000000000000..7b5aee200c8e0
--- /dev/null
+++ b/integration-tests/gradle/src/main/resources/grpc-multi-module-no-java/settings.gradle
@@ -0,0 +1,22 @@
+pluginManagement {
+ repositories {
+ mavenLocal {
+ content {
+ includeGroupByRegex 'io.quarkus.*'
+ includeGroup 'org.hibernate.orm'
+ }
+ }
+ mavenCentral()
+ gradlePluginPortal()
+ }
+ //noinspection GroovyAssignabilityCheck
+ plugins {
+ id 'io.quarkus' version "${quarkusPluginVersion}"
+ }
+}
+
+rootProject.name = 'quarkus-grpc-multi-module-no-java'
+
+include ':module1'
+include ':application'
+
diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/GrpcMultiModuleNoJavaQuarkusBuildTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/GrpcMultiModuleNoJavaQuarkusBuildTest.java
new file mode 100644
index 0000000000000..e31a2b8e1b78d
--- /dev/null
+++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/GrpcMultiModuleNoJavaQuarkusBuildTest.java
@@ -0,0 +1,27 @@
+package io.quarkus.gradle;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.File;
+import java.nio.file.Path;
+
+import org.junit.jupiter.api.Test;
+
+public class GrpcMultiModuleNoJavaQuarkusBuildTest extends QuarkusGradleWrapperTestBase {
+
+ @Test
+ public void testGrpcMultiModuleBuild() throws Exception {
+
+ final File projectDir = getProjectDir("grpc-multi-module-no-java");
+
+ final BuildResult build = runGradleWrapper(projectDir, "clean", "build");
+ assertThat(BuildResult.isSuccessful(build.getTasks().get(":application:quarkusBuild"))).isTrue();
+ assertThat(BuildResult.isSuccessful(build.getTasks().get(":application:quarkusAppPartsBuild"))).isTrue();
+
+ final Path applicationLib = projectDir.toPath().resolve("application").resolve("build").resolve("quarkus-app")
+ .resolve("lib").resolve("main");
+ assertThat(applicationLib).exists();
+ assertThat(applicationLib.resolve("org.acme.module1-1.0.0-SNAPSHOT.jar")).exists();
+ }
+
+}
diff --git a/integration-tests/vertx-http-compressors/all/src/main/java/io/quarkus/compressors/it/AllDecompressResource.java b/integration-tests/vertx-http-compressors/all/src/main/java/io/quarkus/compressors/it/AllDecompressResource.java
new file mode 100644
index 0000000000000..43f8794d8f3cd
--- /dev/null
+++ b/integration-tests/vertx-http-compressors/all/src/main/java/io/quarkus/compressors/it/AllDecompressResource.java
@@ -0,0 +1,7 @@
+package io.quarkus.compressors.it;
+
+import jakarta.ws.rs.Path;
+
+@Path("/decompressed")
+public class AllDecompressResource extends DecompressResource {
+}
diff --git a/integration-tests/vertx-http-compressors/all/src/test/java/io/quarkus/compressors/it/RESTEndpointsTest.java b/integration-tests/vertx-http-compressors/all/src/test/java/io/quarkus/compressors/it/RESTEndpointsTest.java
index 1786ff5b8e7f2..e2a59400de177 100644
--- a/integration-tests/vertx-http-compressors/all/src/test/java/io/quarkus/compressors/it/RESTEndpointsTest.java
+++ b/integration-tests/vertx-http-compressors/all/src/test/java/io/quarkus/compressors/it/RESTEndpointsTest.java
@@ -1,6 +1,7 @@
package io.quarkus.compressors.it;
-import static io.quarkus.compressors.it.Testflow.runTest;
+import static io.quarkus.compressors.it.Testflow.runCompressorsTest;
+import static io.quarkus.compressors.it.Testflow.runDecompressorsTest;
import java.net.URL;
@@ -14,7 +15,10 @@
public class RESTEndpointsTest {
@TestHTTPResource(value = "/compressed")
- URL url;
+ URL urlCompressed;
+
+ @TestHTTPResource(value = "/decompressed")
+ URL urlDEcompressed;
@ParameterizedTest
@CsvSource(value = {
@@ -31,6 +35,88 @@ public class RESTEndpointsTest {
//@formatter:on
}, delimiter = '|', ignoreLeadingAndTrailingWhitespace = true, nullValues = "null")
public void testCompressors(String endpoint, String acceptEncoding, String contentEncoding, String contentLength) {
- runTest(url.toString() + endpoint, acceptEncoding, contentEncoding, contentLength);
+ runCompressorsTest(urlCompressed.toString() + endpoint, acceptEncoding, contentEncoding, contentLength);
+ }
+
+ @ParameterizedTest
+ @CsvSource(value = {
+ //@formatter:off
+ // Context | Accept-Encoding | Content-Encoding | Method
+ "/text | identity | br | POST",
+ "/text | identity | gzip | POST",
+ "/text | identity | deflate | POST",
+ "/text | identity | br | PUT",
+ "/text | identity | gzip | PUT",
+ "/text | identity | deflate | PUT",
+ "/text | deflate | br | POST",
+ "/text | deflate | gzip | POST",
+ "/text | deflate | deflate | POST",
+ "/text | gzip | br | PUT",
+ "/text | gzip | gzip | PUT",
+ "/text | gzip | deflate | PUT",
+ "/text | br | br | POST",
+ "/text | br | gzip | POST",
+ "/text | br | deflate | POST",
+ "/text | br | br | PUT",
+ "/text | br | gzip | PUT",
+ "/text | gzip,br,deflate | deflate | PUT",
+ "/json | identity | br | POST",
+ "/json | identity | gzip | POST",
+ "/json | identity | deflate | POST",
+ "/json | identity | br | PUT",
+ "/json | identity | gzip | PUT",
+ "/json | identity | deflate | PUT",
+ "/json | deflate | br | POST",
+ "/json | deflate | gzip | POST",
+ "/json | deflate | deflate | POST",
+ "/json | gzip | br | PUT",
+ "/json | gzip | gzip | PUT",
+ "/json | gzip | deflate | PUT",
+ "/json | br | br | POST",
+ "/json | br | gzip | POST",
+ "/json | br | deflate | POST",
+ "/json | br | br | PUT",
+ "/json | br | gzip | PUT",
+ "/json | gzip,br,deflate | deflate | PUT",
+ "/xml | identity | br | POST",
+ "/xml | identity | gzip | POST",
+ "/xml | identity | deflate | POST",
+ "/xml | identity | br | PUT",
+ "/xml | identity | gzip | PUT",
+ "/xml | identity | deflate | PUT",
+ "/xml | deflate | br | POST",
+ "/xml | deflate | gzip | POST",
+ "/xml | deflate | deflate | POST",
+ "/xml | gzip | br | PUT",
+ "/xml | gzip | gzip | PUT",
+ "/xml | gzip | deflate | PUT",
+ "/xml | br | br | POST",
+ "/xml | br | gzip | POST",
+ "/xml | br | deflate | POST",
+ "/xml | br | br | PUT",
+ "/xml | br | gzip | PUT",
+ "/xml | gzip,br,deflate | deflate | PUT",
+ "/xhtml | identity | br | POST",
+ "/xhtml | identity | gzip | POST",
+ "/xhtml | identity | deflate | POST",
+ "/xhtml | identity | br | PUT",
+ "/xhtml | identity | gzip | PUT",
+ "/xhtml | identity | deflate | PUT",
+ "/xhtml | deflate | br | POST",
+ "/xhtml | deflate | gzip | POST",
+ "/xhtml | deflate | deflate | POST",
+ "/xhtml | gzip | br | PUT",
+ "/xhtml | gzip | gzip | PUT",
+ "/xhtml | gzip | deflate | PUT",
+ "/xhtml | br | br | POST",
+ "/xhtml | br | gzip | POST",
+ "/xhtml | br | deflate | POST",
+ "/xhtml | br | br | PUT",
+ "/xhtml | br | gzip | PUT",
+ "/xhtml | gzip,br,deflate | deflate | PUT"
+ //@formatter:on
+ }, delimiter = '|', ignoreLeadingAndTrailingWhitespace = true, nullValues = "null")
+ public void testDecompressors(String endpoint, String acceptEncoding, String contentEncoding, String method) {
+ runDecompressorsTest(urlDEcompressed.toString() + endpoint, acceptEncoding, contentEncoding, method);
}
}
diff --git a/integration-tests/vertx-http-compressors/all/src/test/resources/application.properties b/integration-tests/vertx-http-compressors/all/src/test/resources/application.properties
index d7bbc1ca381a0..9e910a8390a20 100644
--- a/integration-tests/vertx-http-compressors/all/src/test/resources/application.properties
+++ b/integration-tests/vertx-http-compressors/all/src/test/resources/application.properties
@@ -1,4 +1,7 @@
+# Enables sending clients compressed responses.
quarkus.http.enable-compression=true
+# Enables decompressing requests from clients.
+quarkus.http.enable-decompression=true
# Brotli is not present by default, so we add it all here:
quarkus.http.compressors=deflate,gzip,br
# This test the level actually makes impact. When left to default,
diff --git a/integration-tests/vertx-http-compressors/app/src/main/java/io/quarkus/compressors/it/DecompressResource.java b/integration-tests/vertx-http-compressors/app/src/main/java/io/quarkus/compressors/it/DecompressResource.java
new file mode 100644
index 0000000000000..6e1a19bd6f18f
--- /dev/null
+++ b/integration-tests/vertx-http-compressors/app/src/main/java/io/quarkus/compressors/it/DecompressResource.java
@@ -0,0 +1,90 @@
+package io.quarkus.compressors.it;
+
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+
+/**
+ * Resource with endpoints that consume compressed data
+ * in POST and PUT bodies from the client.
+ * Depending on the accept-encoding, the data is then
+ * compressed again and sent to the client
+ *
+ * e.g. Client sends a gzipped POST body and receives
+ * a brotli compressed response body.
+ *
+ * The endpoint looks like a dummy echo service, but
+ * there is compression and decompression going on behind
+ * the scenes in Vert.x. -> Netty.
+ *
+ * See: https://github.com/quarkusio/quarkus/pull/44348
+ */
+public class DecompressResource {
+
+ @POST
+ @Path("/text")
+ @Consumes(MediaType.TEXT_PLAIN)
+ @Produces(MediaType.TEXT_PLAIN)
+ public String textPost(String text) {
+ return text;
+ }
+
+ @PUT
+ @Path("/text")
+ @Consumes(MediaType.TEXT_PLAIN)
+ @Produces(MediaType.TEXT_PLAIN)
+ public String textPut(String text) {
+ return text;
+ }
+
+ @POST
+ @Path("/json")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public String jsonPost(String json) {
+ return json;
+ }
+
+ @PUT
+ @Path("/json")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public String jsonPut(String json) {
+ return json;
+ }
+
+ @POST
+ @Path("/xml")
+ @Produces(MediaType.TEXT_XML)
+ @Consumes(MediaType.TEXT_XML)
+ public String xmlPost(String xml) {
+ return xml;
+ }
+
+ @PUT
+ @Path("/xml")
+ @Produces(MediaType.TEXT_XML)
+ @Consumes(MediaType.TEXT_XML)
+ public String xmlPut(String xml) {
+ return xml;
+ }
+
+ @POST
+ @Path("/xhtml")
+ @Produces(MediaType.APPLICATION_XHTML_XML)
+ @Consumes(MediaType.APPLICATION_XHTML_XML)
+ public String xhtmlPost(String xhtml) {
+ return xhtml;
+ }
+
+ @PUT
+ @Path("/xhtml")
+ @Produces(MediaType.APPLICATION_XHTML_XML)
+ @Consumes(MediaType.APPLICATION_XHTML_XML)
+ public String xhtmlPut(String xhtml) {
+ return xhtml;
+ }
+}
diff --git a/integration-tests/vertx-http-compressors/app/src/test/java/io/quarkus/compressors/it/Testflow.java b/integration-tests/vertx-http-compressors/app/src/test/java/io/quarkus/compressors/it/Testflow.java
index 378486123faf9..3f1e72cf6eb71 100644
--- a/integration-tests/vertx-http-compressors/app/src/test/java/io/quarkus/compressors/it/Testflow.java
+++ b/integration-tests/vertx-http-compressors/app/src/test/java/io/quarkus/compressors/it/Testflow.java
@@ -9,9 +9,16 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.GZIPOutputStream;
+
+import com.aayushatharva.brotli4j.Brotli4jLoader;
+import com.aayushatharva.brotli4j.encoder.BrotliOutputStream;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
@@ -33,16 +40,21 @@ public class Testflow {
// Vert.x/Netty versions over time.
public static final int COMPRESSION_TOLERANCE_PERCENT = 2;
+ static {
+ // Our test code does compression
+ Brotli4jLoader.ensureAvailability();
+ }
+
/**
- * This test logic is shared by both "all" module and "some" module.
- * See their RESTEndpointsTest classes.
+ * This test logic is shared by both "all" module and "some" module. See their RESTEndpointsTest classes.
*
* @param endpoint
* @param acceptEncoding
* @param contentEncoding
* @param contentLength
*/
- public static void runTest(String endpoint, String acceptEncoding, String contentEncoding, String contentLength) {
+ public static void runCompressorsTest(String endpoint, String acceptEncoding, String contentEncoding,
+ String contentLength) {
LOG.infof("Endpoint %s; Accept-Encoding: %s; Content-Encoding: %s; Content-Length: %s",
endpoint, acceptEncoding, contentEncoding, contentLength);
// RestAssured
@@ -97,31 +109,89 @@ public static void runTest(String endpoint, String acceptEncoding, String conten
expectedLength + " plus " + COMPRESSION_TOLERANCE_PERCENT + "% tolerance, i.e. "
+ expectedLengthWithTolerance + ".");
}
+ assertEquals(TEXT, decompress(actualEncoding, response.body().getBytes()), "Unexpected body text.");
+ } catch (InterruptedException | ExecutionException e) {
+ fail(e);
+ }
+ }
- final String body;
- if (actualEncoding != null && !"identity".equalsIgnoreCase(actualEncoding)) {
- EmbeddedChannel channel = null;
- if ("gzip".equalsIgnoreCase(actualEncoding)) {
- channel = new EmbeddedChannel(newZlibDecoder(ZlibWrapper.GZIP));
- } else if ("deflate".equalsIgnoreCase(actualEncoding)) {
- channel = new EmbeddedChannel(newZlibDecoder(ZlibWrapper.ZLIB));
- } else if ("br".equalsIgnoreCase(actualEncoding)) {
- channel = new EmbeddedChannel(new BrotliDecoder());
- } else {
- fail("Unexpected compression used by server: " + actualEncoding);
- }
- channel.writeInbound(Unpooled.copiedBuffer(response.body().getBytes()));
- channel.finish();
- final ByteBuf decompressed = channel.readInbound();
- body = decompressed.readCharSequence(decompressed.readableBytes(), StandardCharsets.UTF_8).toString();
- } else {
- body = response.body().toString(StandardCharsets.UTF_8);
- }
-
- assertEquals(TEXT, body,
- "Unexpected body text.");
+ public static void runDecompressorsTest(String endpoint, String acceptEncoding, String contentEncoding,
+ String method) {
+ LOG.infof("Endpoint %s; Accept-Encoding: %s; Content-Encoding: %s; Method: %s",
+ endpoint, acceptEncoding, contentEncoding, method);
+ final WebClient client = WebClient.create(Vertx.vertx(), new WebClientOptions()
+ .setLogActivity(true)
+ .setFollowRedirects(true)
+ .setDecompressionSupported(false));
+ final CompletableFuture> future = new CompletableFuture<>();
+ client.postAbs(endpoint)
+ .putHeader(HttpHeaders.CONTENT_ENCODING.toString(), contentEncoding)
+ .putHeader(HttpHeaders.ACCEPT.toString(), "*/*")
+ .putHeader(HttpHeaders.USER_AGENT.toString(), "Tester")
+ .sendBuffer(compress(contentEncoding, TEXT), ar -> {
+ if (ar.succeeded()) {
+ future.complete(ar.result());
+ } else {
+ future.completeExceptionally(ar.cause());
+ }
+ });
+ try {
+ final HttpResponse response = future.get();
+ final String actualEncoding = response.headers().get("content-encoding");
+ final String body = decompress(actualEncoding, response.body().getBytes());
+ assertEquals(OK.code(), response.statusCode(), "Http status must be OK.");
+ assertEquals(TEXT, body, "Unexpected body text.");
} catch (InterruptedException | ExecutionException e) {
fail(e);
}
}
+
+ public static Buffer compress(String algorithm, String payload) {
+ final ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ if ("gzip".equalsIgnoreCase(algorithm)) {
+ try (GZIPOutputStream gzipStream = new GZIPOutputStream(byteStream)) {
+ gzipStream.write(payload.getBytes(StandardCharsets.UTF_8));
+ } catch (IOException e) {
+ throw new RuntimeException("Gzip compression failed", e);
+ }
+ return Buffer.buffer(byteStream.toByteArray());
+ } else if ("br".equalsIgnoreCase(algorithm)) {
+ try (BrotliOutputStream brotliStream = new BrotliOutputStream(byteStream)) {
+ brotliStream.write(payload.getBytes(StandardCharsets.UTF_8));
+ } catch (IOException e) {
+ throw new RuntimeException("Brotli compression failed", e);
+ }
+ return Buffer.buffer(byteStream.toByteArray());
+ } else if ("deflate".equalsIgnoreCase(algorithm)) {
+ try (DeflaterOutputStream deflateStream = new DeflaterOutputStream(byteStream)) {
+ deflateStream.write(payload.getBytes(StandardCharsets.UTF_8));
+ } catch (IOException e) {
+ throw new RuntimeException("Deflate compression failed", e);
+ }
+ return Buffer.buffer(byteStream.toByteArray());
+ } else {
+ throw new IllegalArgumentException("Unsupported encoding: " + algorithm);
+ }
+ }
+
+ public static String decompress(String algorithm, byte[] payload) {
+ if (algorithm != null && !"identity".equalsIgnoreCase(algorithm)) {
+ final EmbeddedChannel channel;
+ if ("gzip".equalsIgnoreCase(algorithm)) {
+ channel = new EmbeddedChannel(newZlibDecoder(ZlibWrapper.GZIP));
+ } else if ("deflate".equalsIgnoreCase(algorithm)) {
+ channel = new EmbeddedChannel(newZlibDecoder(ZlibWrapper.ZLIB));
+ } else if ("br".equalsIgnoreCase(algorithm)) {
+ channel = new EmbeddedChannel(new BrotliDecoder());
+ } else {
+ throw new RuntimeException("Unexpected compression used by server: " + algorithm);
+ }
+ channel.writeInbound(Unpooled.copiedBuffer(payload));
+ channel.finish();
+ final ByteBuf decompressed = channel.readInbound();
+ return decompressed.readCharSequence(decompressed.readableBytes(), StandardCharsets.UTF_8).toString();
+ } else {
+ return new String(payload, StandardCharsets.UTF_8);
+ }
+ }
}
diff --git a/integration-tests/vertx-http-compressors/some/src/test/java/io/quarkus/compressors/it/RESTEndpointsTest.java b/integration-tests/vertx-http-compressors/some/src/test/java/io/quarkus/compressors/it/RESTEndpointsTest.java
index d2f9efc94e39c..9763e151fd69f 100644
--- a/integration-tests/vertx-http-compressors/some/src/test/java/io/quarkus/compressors/it/RESTEndpointsTest.java
+++ b/integration-tests/vertx-http-compressors/some/src/test/java/io/quarkus/compressors/it/RESTEndpointsTest.java
@@ -1,6 +1,6 @@
package io.quarkus.compressors.it;
-import static io.quarkus.compressors.it.Testflow.runTest;
+import static io.quarkus.compressors.it.Testflow.runCompressorsTest;
import java.net.URL;
@@ -30,6 +30,6 @@ public class RESTEndpointsTest {
//@formatter:on
}, delimiter = '|', ignoreLeadingAndTrailingWhitespace = true, nullValues = "null")
public void testCompressors(String endpoint, String acceptEncoding, String contentEncoding, String contentLength) {
- runTest(url.toString() + endpoint, acceptEncoding, contentEncoding, contentLength);
+ runCompressorsTest(url.toString() + endpoint, acceptEncoding, contentEncoding, contentLength);
}
}
diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java
index 36500263f4638..a36ee0cc8ac64 100644
--- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java
+++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java
@@ -14,6 +14,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -21,6 +22,8 @@
import org.jboss.jandex.Index;
import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.extension.ConditionEvaluationResult;
+import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.ExtensionContext;
import io.quarkus.bootstrap.BootstrapConstants;
@@ -41,7 +44,8 @@
import io.quarkus.test.common.RestorableSystemProperties;
import io.quarkus.test.common.TestClassIndexer;
-public class AbstractJvmQuarkusTestExtension extends AbstractQuarkusTestWithContextExtension {
+public class AbstractJvmQuarkusTestExtension extends AbstractQuarkusTestWithContextExtension
+ implements ExecutionCondition {
protected static final String TEST_LOCATION = "test-location";
protected static final String TEST_CLASS = "test-class";
@@ -267,6 +271,42 @@ private Class extends QuarkusTestProfile> findTestProfileAnnotation(Class> c
return null;
}
+ @Override
+ public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
+ if (!context.getTestClass().isPresent()) {
+ return ConditionEvaluationResult.enabled("No test class specified");
+ }
+ if (context.getTestInstance().isPresent()) {
+ return ConditionEvaluationResult.enabled("Quarkus Test Profile tags only affect classes");
+ }
+ String tagsStr = System.getProperty("quarkus.test.profile.tags");
+ if ((tagsStr == null) || tagsStr.isEmpty()) {
+ return ConditionEvaluationResult.enabled("No Quarkus Test Profile tags");
+ }
+ Class extends QuarkusTestProfile> testProfile = getQuarkusTestProfile(context);
+ if (testProfile == null) {
+ return ConditionEvaluationResult.disabled("Test '" + context.getRequiredTestClass()
+ + "' is not annotated with '@QuarkusTestProfile' but 'quarkus.profile.test.tags' was set");
+ }
+ QuarkusTestProfile profileInstance;
+ try {
+ profileInstance = testProfile.getConstructor().newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ Set testProfileTags = profileInstance.tags();
+ String[] tags = tagsStr.split(",");
+ for (String tag : tags) {
+ String trimmedTag = tag.trim();
+ if (testProfileTags.contains(trimmedTag)) {
+ return ConditionEvaluationResult.enabled("Tag '" + trimmedTag + "' is present on '" + testProfile
+ + "' which is used on test '" + context.getRequiredTestClass());
+ }
+ }
+ return ConditionEvaluationResult.disabled("Test '" + context.getRequiredTestClass()
+ + "' disabled because 'quarkus.profile.test.tags' don't match the tags of '" + testProfile + "'");
+ }
+
protected static class PrepareResult {
protected final AugmentAction augmentAction;
protected final QuarkusTestProfile profileInstance;
diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusMainTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusMainTestExtension.java
index f68b87436684b..71776250160ec 100644
--- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusMainTestExtension.java
+++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusMainTestExtension.java
@@ -16,6 +16,8 @@
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ConditionEvaluationResult;
+import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.InvocationInterceptor;
import org.junit.jupiter.api.extension.ParameterContext;
@@ -38,7 +40,7 @@
public class QuarkusMainTestExtension extends AbstractJvmQuarkusTestExtension
implements InvocationInterceptor, BeforeEachCallback, AfterEachCallback, ParameterResolver, BeforeAllCallback,
- AfterAllCallback {
+ AfterAllCallback, ExecutionCondition {
PrepareResult prepareResult;
@@ -321,4 +323,9 @@ public void afterAll(ExtensionContext context) throws Exception {
public void beforeAll(ExtensionContext context) throws Exception {
currentTestClassStack.push(context.getRequiredTestClass());
}
+
+ @Override
+ public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
+ return super.evaluateExecutionCondition(context);
+ }
}
diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java
index 8afb99cf10bb4..91da01372bb78 100644
--- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java
+++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java
@@ -24,7 +24,6 @@
import java.util.Objects;
import java.util.Optional;
import java.util.ServiceLoader;
-import java.util.Set;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
@@ -54,8 +53,6 @@
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
-import org.junit.jupiter.api.extension.ConditionEvaluationResult;
-import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.InvocationInterceptor;
import org.junit.jupiter.api.extension.ParameterContext;
@@ -105,7 +102,7 @@
public class QuarkusTestExtension extends AbstractJvmQuarkusTestExtension
implements BeforeEachCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback, AfterEachCallback,
BeforeAllCallback, InvocationInterceptor, AfterAllCallback,
- ParameterResolver, ExecutionCondition {
+ ParameterResolver {
private static final Logger log = Logger.getLogger(QuarkusTestExtension.class);
@@ -1136,42 +1133,6 @@ private boolean testMethodInvokerHandlesParamType(Object testMethodInvoker, Para
}
}
- @Override
- public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
- if (!context.getTestClass().isPresent()) {
- return ConditionEvaluationResult.enabled("No test class specified");
- }
- if (context.getTestInstance().isPresent()) {
- return ConditionEvaluationResult.enabled("Quarkus Test Profile tags only affect classes");
- }
- String tagsStr = System.getProperty("quarkus.test.profile.tags");
- if ((tagsStr == null) || tagsStr.isEmpty()) {
- return ConditionEvaluationResult.enabled("No Quarkus Test Profile tags");
- }
- Class extends QuarkusTestProfile> testProfile = getQuarkusTestProfile(context);
- if (testProfile == null) {
- return ConditionEvaluationResult.disabled("Test '" + context.getRequiredTestClass()
- + "' is not annotated with '@QuarkusTestProfile' but 'quarkus.profile.test.tags' was set");
- }
- QuarkusTestProfile profileInstance;
- try {
- profileInstance = testProfile.getConstructor().newInstance();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- Set testProfileTags = profileInstance.tags();
- String[] tags = tagsStr.split(",");
- for (String tag : tags) {
- String trimmedTag = tag.trim();
- if (testProfileTags.contains(trimmedTag)) {
- return ConditionEvaluationResult.enabled("Tag '" + trimmedTag + "' is present on '" + testProfile
- + "' which is used on test '" + context.getRequiredTestClass());
- }
- }
- return ConditionEvaluationResult.disabled("Test '" + context.getRequiredTestClass()
- + "' disabled because 'quarkus.profile.test.tags' don't match the tags of '" + testProfile + "'");
- }
-
public static class ExtensionState extends QuarkusTestExtensionState {
public ExtensionState(Closeable testResourceManager, Closeable resource, Runnable clearCallbacks) {