From 378e48a505eb440a8b6c401df02c4f11c6fa51bb Mon Sep 17 00:00:00 2001 From: luneo7 Date: Wed, 18 May 2022 17:12:24 -0500 Subject: [PATCH 01/14] Support MDC when using OTEL --- docs/src/main/asciidoc/opentelemetry.adoc | 14 +- .../deployment/OpenTelemetryMDCTest.java | 187 ++++++++++++++++++ .../runtime/MDCEnabledContextStorage.java | 38 ++++ .../runtime/OpenTelemetryUtil.java | 48 +++++ .../runtime/QuarkusContextStorage.java | 12 +- 5 files changed, 294 insertions(+), 5 deletions(-) create mode 100644 extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryMDCTest.java create mode 100644 extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/MDCEnabledContextStorage.java diff --git a/docs/src/main/asciidoc/opentelemetry.adoc b/docs/src/main/asciidoc/opentelemetry.adoc index e1425fecb6d57..15bd64498b475 100644 --- a/docs/src/main/asciidoc/opentelemetry.adoc +++ b/docs/src/main/asciidoc/opentelemetry.adoc @@ -107,13 +107,15 @@ quarkus.opentelemetry.enabled=true // <2> quarkus.opentelemetry.tracer.exporter.otlp.endpoint=http://localhost:4317 // <3> quarkus.opentelemetry.tracer.exporter.otlp.headers=Authorization=Bearer my_secret // <4> + +quarkus.log.console.format=%d{HH:mm:ss} %-5p traceId=%X{traceId}, parentId=%X{parentId}, spanId=%X{spanId}, sampled=%X{sampled} [%c{2.}] (%t) %s%e%n // <5> ---- <1> All spans created from the application will include an OpenTelemetry `Resource` indicating the span was created by the `myservice` application. If not set, it will default to the artifact id. <2> Whether OpenTelemetry is enabled or not. The default is `true`, but shown here to indicate how it can be disabled <3> gRPC endpoint for sending spans <4> Optional gRPC headers commonly used for authentication - +<5> Add tracing information into log message. == Run the application The first step is to configure and start the https://opentelemetry.io/docs/collector/[OpenTelemetry Collector] to receive, process and export telemetry data to https://www.jaegertracing.io/[Jaeger] that will display the captured traces. @@ -199,6 +201,16 @@ $ curl http://localhost:8080/hello hello ---- +When the first request has been submitted, you will be able to see the tracing information in the logs: + +[source] +---- +10:49:02 INFO traceId=, parentId=, spanId=, sampled= [io.quarkus] (main) Installed features: [cdi, opentelemetry, opentelemetry-otlp-exporter, rest-client, resteasy, smallrye-context-propagation, vertx] +10:49:03 INFO traceId=17ceb8429b9f25b0b879fa1503259456, parentId=3125c8bee75b7ad6, spanId=58ce77c86dd23457, sampled=true [or.ac.op.TracedResource] (executor-thread-1) hello +10:49:03 INFO traceId=ad23acd6d9a4ed3d1de07866a52fa2df, parentId=, spanId=df13f5b45cf4d1e2, sampled=true [or.ac.op.TracedResource] (executor-thread-0) hello +---- + + Then visit the http://localhost:16686[Jaeger UI] to see the tracing information. Hit `CTRL+C` to stop the application. diff --git a/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryMDCTest.java b/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryMDCTest.java new file mode 100644 index 0000000000000..30b114d42cacd --- /dev/null +++ b/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryMDCTest.java @@ -0,0 +1,187 @@ +package io.quarkus.opentelemetry.deployment; + +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import org.jboss.logging.MDC; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Scope; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.quarkus.arc.Unremovable; +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +public class OpenTelemetryMDCTest { + @RegisterExtension + static final QuarkusUnitTest unitTest = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClass(MdcEntry.class) + .addClass(TestMdcCapturer.class) + .addClass(TestSpanExporter.class) + .addClass(TestResource.class)); + + @Inject + TestSpanExporter spanExporter; + @Inject + TestMdcCapturer testMdcCapturer; + @Inject + Tracer tracer; + + @AfterEach + void tearDown() { + spanExporter.reset(); + testMdcCapturer.reset(); + } + + @Test + void vertx() { + RestAssured.when() + .get("/hello").then() + .statusCode(200) + .body(is("hello")); + + List spans = spanExporter.getFinishedSpanItems(2); + + List mdcEntries = testMdcCapturer.getCapturedMdcEntries(); + + List expectedMdcEntries = getExpectedMDCEntries(spans); + + assertEquals("something", spans.get(0).getName()); + assertEquals("/hello", spans.get(1).getName()); + assertEquals(expectedMdcEntries, mdcEntries); + } + + @Test + void nonVertx() { + Span parentSpan = tracer.spanBuilder("parent").startSpan(); + try (Scope ignored = parentSpan.makeCurrent()) { + testMdcCapturer.captureMdc(); + Span childSpan = tracer.spanBuilder("child").startSpan(); + try (Scope ignored1 = childSpan.makeCurrent()) { + testMdcCapturer.captureMdc(); + } finally { + childSpan.end(); + } + } finally { + parentSpan.end(); + } + + List spans = spanExporter.getFinishedSpanItems(2); + + List mdcEntries = testMdcCapturer.getCapturedMdcEntries(); + + List expectedMdcEntries = getExpectedMDCEntries(spans); + + assertEquals("child", spans.get(0).getName()); + assertEquals("parent", spans.get(1).getName()); + assertEquals(expectedMdcEntries, mdcEntries); + } + + private List getExpectedMDCEntries(List spans) { + return spans.stream() + .map(spanData -> new MdcEntry(spanData.getSpanContext().isSampled(), + spanData.getParentSpanContext().isValid() ? spanData.getParentSpanId() : "null", + spanData.getSpanId(), + spanData.getTraceId())) + .collect(Collectors.collectingAndThen(Collectors.toList(), l -> { + Collections.reverse(l); + return l; + })); + } + + @ApplicationScoped + @Path("/") + public static class TestResource { + + @Inject + TestMdcCapturer testMdcCapturer; + + @Inject + Tracer tracer; + + @GET + @Path("/hello") + public String hello() { + testMdcCapturer.captureMdc(); + Span span = tracer.spanBuilder("something").startSpan(); + try (Scope ignored = span.makeCurrent()) { + testMdcCapturer.captureMdc(); + } finally { + span.end(); + } + return "hello"; + } + } + + @Unremovable + @ApplicationScoped + public static class TestMdcCapturer { + private final List mdcEntries = Collections.synchronizedList(new ArrayList<>()); + + public void reset() { + mdcEntries.clear(); + } + + public void captureMdc() { + mdcEntries.add(new MdcEntry( + Boolean.parseBoolean(String.valueOf(MDC.get("sampled"))), + String.valueOf(MDC.get("parentId")), + String.valueOf(MDC.get("spanId")), + String.valueOf(MDC.get("traceId")))); + } + + public List getCapturedMdcEntries() { + return List.copyOf(mdcEntries); + } + } + + public static class MdcEntry { + public final boolean isSampled; + public final String parentId; + public final String spanId; + public final String traceId; + + public MdcEntry(boolean isSampled, String parentId, String spanId, String traceId) { + this.isSampled = isSampled; + this.parentId = parentId; + this.spanId = spanId; + this.traceId = traceId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof MdcEntry)) { + return false; + } + MdcEntry mdcEntry = (MdcEntry) o; + return isSampled == mdcEntry.isSampled && + Objects.equals(parentId, mdcEntry.parentId) && + Objects.equals(spanId, mdcEntry.spanId) && + Objects.equals(traceId, mdcEntry.traceId); + } + + @Override + public int hashCode() { + return Objects.hash(isSampled, parentId, spanId, traceId); + } + } +} diff --git a/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/MDCEnabledContextStorage.java b/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/MDCEnabledContextStorage.java new file mode 100644 index 0000000000000..c3d357e24fc6d --- /dev/null +++ b/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/MDCEnabledContextStorage.java @@ -0,0 +1,38 @@ +package io.quarkus.opentelemetry.runtime; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextStorage; +import io.opentelemetry.context.Scope; + +/** + * A Context Storage that wraps the default OpenTelemetry ContextStorage and + * adds MDC functionality. + */ +enum MDCEnabledContextStorage implements ContextStorage { + INSTANCE; + + private static final ContextStorage DEFAULT_CONTEXT_STORAGE = ContextStorage.defaultStorage(); + + @Override + public Scope attach(Context toAttach) { + Context beforeAttach = current(); + + OpenTelemetryUtil.setMDCData(toAttach, null); + + Scope scope = DEFAULT_CONTEXT_STORAGE.attach(toAttach); + + return () -> { + if (beforeAttach == null) { + OpenTelemetryUtil.clearMDCData(null); + } else { + OpenTelemetryUtil.setMDCData(beforeAttach, null); + } + scope.close(); + }; + } + + @Override + public Context current() { + return DEFAULT_CONTEXT_STORAGE.current(); + } +} diff --git a/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/OpenTelemetryUtil.java b/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/OpenTelemetryUtil.java index a4eb7b180f8e4..c406794ba449d 100644 --- a/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/OpenTelemetryUtil.java +++ b/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/OpenTelemetryUtil.java @@ -11,12 +11,22 @@ import java.util.stream.StreamSupport; import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.context.Context; import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurablePropagatorProvider; +import io.opentelemetry.sdk.trace.ReadableSpan; +import io.quarkus.vertx.core.runtime.VertxMDC; public final class OpenTelemetryUtil { + public static final String TRACE_ID = "traceId"; + public static final String SPAN_ID = "spanId"; + public static final String SAMPLED = "sampled"; + public static final String PARENT_ID = "parentId"; + private OpenTelemetryUtil() { } @@ -74,4 +84,42 @@ public static Map convertKeyValueListToMap(List headers) .map(keyValuePair -> new AbstractMap.SimpleImmutableEntry<>(keyValuePair[0].trim(), keyValuePair[1].trim())) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (first, next) -> next, LinkedHashMap::new)); } + + /** + * Sets MDC data by using the current span from the context. + * + * @param context opentelemetry context + * @param vertxContext vertx context + */ + public static void setMDCData(Context context, io.vertx.core.Context vertxContext) { + Span span = Span.fromContextOrNull(context); + if (span != null) { + SpanContext spanContext = span.getSpanContext(); + VertxMDC vertxMDC = VertxMDC.INSTANCE; + vertxMDC.put(SPAN_ID, spanContext.getSpanId(), vertxContext); + vertxMDC.put(TRACE_ID, spanContext.getTraceId(), vertxContext); + vertxMDC.put(SAMPLED, Boolean.toString(spanContext.isSampled()), vertxContext); + if (span instanceof ReadableSpan) { + SpanContext parentSpanContext = ((ReadableSpan) span).getParentSpanContext(); + if (parentSpanContext.isValid()) { + vertxMDC.put(PARENT_ID, parentSpanContext.getSpanId(), vertxContext); + } else { + vertxMDC.remove(PARENT_ID, vertxContext); + } + } + } + } + + /** + * Clears MDC data related to OpenTelemetry + * + * @param vertxContext vertx context + */ + public static void clearMDCData(io.vertx.core.Context vertxContext) { + VertxMDC vertxMDC = VertxMDC.INSTANCE; + vertxMDC.remove(TRACE_ID, vertxContext); + vertxMDC.remove(SPAN_ID, vertxContext); + vertxMDC.remove(PARENT_ID, vertxContext); + vertxMDC.remove(SAMPLED, vertxContext); + } } diff --git a/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/QuarkusContextStorage.java b/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/QuarkusContextStorage.java index fb8b1d0ad1adb..7452f6dcfcf8a 100644 --- a/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/QuarkusContextStorage.java +++ b/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/QuarkusContextStorage.java @@ -14,7 +14,8 @@ /** * Bridges the OpenTelemetry ContextStorage with the Vert.x Context. The default OpenTelemetry ContextStorage (based in * ThreadLocals) is not suitable for Vert.x. In this case, the OpenTelemetry Context piggybacks on top of the Vert.x - * Context. If the Vert.x Context is not available, fallbacks to the default OpenTelemetry ContextStorage. + * Context. If the Vert.x Context is not available, fallbacks to an MDC enabled context storage that wraps the default + * OpenTelemetry ContextStorage. */ public enum QuarkusContextStorage implements ContextStorage { INSTANCE; @@ -22,7 +23,7 @@ public enum QuarkusContextStorage implements ContextStorage { private static final Logger log = Logger.getLogger(QuarkusContextStorage.class); private static final String OTEL_CONTEXT = QuarkusContextStorage.class.getName() + ".otelContext"; - private static final ContextStorage DEFAULT_CONTEXT_STORAGE = ContextStorage.defaultStorage(); + private static final ContextStorage FALLBACK_CONTEXT_STORAGE = MDCEnabledContextStorage.INSTANCE; static Vertx vertx; /** @@ -37,7 +38,7 @@ public enum QuarkusContextStorage implements ContextStorage { public Scope attach(Context toAttach) { io.vertx.core.Context vertxContext = getVertxContext(); return vertxContext != null && isDuplicatedContext(vertxContext) ? attach(vertxContext, toAttach) - : DEFAULT_CONTEXT_STORAGE.attach(toAttach); + : FALLBACK_CONTEXT_STORAGE.attach(toAttach); } /** @@ -64,6 +65,7 @@ public Scope attach(io.vertx.core.Context vertxContext, Context toAttach) { } vertxContext.putLocal(OTEL_CONTEXT, toAttach); + OpenTelemetryUtil.setMDCData(toAttach, vertxContext); return () -> { if (getContext(vertxContext) != toAttach) { @@ -72,8 +74,10 @@ public Scope attach(io.vertx.core.Context vertxContext, Context toAttach) { if (beforeAttach == null) { vertxContext.removeLocal(OTEL_CONTEXT); + OpenTelemetryUtil.clearMDCData(vertxContext); } else { vertxContext.putLocal(OTEL_CONTEXT, beforeAttach); + OpenTelemetryUtil.setMDCData(beforeAttach, vertxContext); } }; } @@ -90,7 +94,7 @@ public Context current() { if (current != null) { return current.getLocal(OTEL_CONTEXT); } else { - return DEFAULT_CONTEXT_STORAGE.current(); + return FALLBACK_CONTEXT_STORAGE.current(); } } From 603b41924bf0f9e95eb7f2ba15e5cdafbc34e88f Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 23 May 2022 09:55:45 +0300 Subject: [PATCH 02/14] Revert "Bump mongodb-crypt from 1.2.1 to 1.4.0" --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 51d9e75518606..c352a449ff65c 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -159,7 +159,7 @@ 1.30 6.0.0 4.3.4 - 1.4.0 + 1.2.1 0.33.10 3.14.9 0.1.1 From a980e86ec118375e31c7f7b8d46ab2bd0c35270c Mon Sep 17 00:00:00 2001 From: Julien Ponge Date: Mon, 23 May 2022 10:57:25 +0200 Subject: [PATCH 03/14] Bump to Mutiny 1.5.0 and Mutiny Vert.x bindings 2.22.0 --- bom/application/pom.xml | 4 ++-- independent-projects/qute/pom.xml | 2 +- independent-projects/resteasy-reactive/pom.xml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 51d9e75518606..740f725bb22f5 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -50,7 +50,7 @@ 1.2.2 1.0.13 2.7.0 - 2.21.0 + 2.22.0 3.16.0 1.1.2 1.2.1 @@ -134,7 +134,7 @@ 4.1.74.Final 1.0.3 3.5.0.Final - 1.4.0 + 1.5.0 3.1.0 1.8.0 1.1.8.4 diff --git a/independent-projects/qute/pom.xml b/independent-projects/qute/pom.xml index d6917d25cf73a..ff0459518cc24 100644 --- a/independent-projects/qute/pom.xml +++ b/independent-projects/qute/pom.xml @@ -46,7 +46,7 @@ 3.5.0.Final 3.0.0-M5 1.6.8 - 1.4.0 + 1.5.0 diff --git a/independent-projects/resteasy-reactive/pom.xml b/independent-projects/resteasy-reactive/pom.xml index 999445da14c05..f4cb0c7ac1d75 100644 --- a/independent-projects/resteasy-reactive/pom.xml +++ b/independent-projects/resteasy-reactive/pom.xml @@ -54,7 +54,7 @@ 1.6.8 2.0.1.Final 1.1.6 - 1.4.0 + 1.5.0 1.12.0 4.2.4 4.5.1 @@ -66,7 +66,7 @@ 1.0.11 1.0.2 4.2.0 - 2.19.0 + 2.22.0 From 542032cfc0091c29fcf94d7ee513dc2f11461eb4 Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Mon, 23 May 2022 12:55:48 +0200 Subject: [PATCH 04/14] Move @Disabled to the class declaration from the method to avoid the test init --- .../catalog/RegistrySnapshotCatalogCompatibilityTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/devtools/src/test/java/io/quarkus/platform/catalog/RegistrySnapshotCatalogCompatibilityTest.java b/integration-tests/devtools/src/test/java/io/quarkus/platform/catalog/RegistrySnapshotCatalogCompatibilityTest.java index a67d1c24f0b72..3d65b8f8f4aff 100644 --- a/integration-tests/devtools/src/test/java/io/quarkus/platform/catalog/RegistrySnapshotCatalogCompatibilityTest.java +++ b/integration-tests/devtools/src/test/java/io/quarkus/platform/catalog/RegistrySnapshotCatalogCompatibilityTest.java @@ -25,6 +25,7 @@ import io.quarkus.registry.catalog.PlatformRelease; import io.quarkus.registry.catalog.PlatformStream; +@Disabled public class RegistrySnapshotCatalogCompatibilityTest { private final ExtensionCatalogResolver catalogResolver = QuarkusProjectHelper.getCatalogResolver(); @@ -33,7 +34,6 @@ public RegistrySnapshotCatalogCompatibilityTest() throws RegistryResolutionExcep } @Test - @Disabled public void testRegistrySnapshotPlatformCatalog() throws RegistryResolutionException, IOException { // TODO Use a local snapshot of the registry for testing final PlatformCatalog platformCatalog = getRegistryPlatformCatalog(); From 1b3550feaead01bda8202788e6e293ee92a3eae3 Mon Sep 17 00:00:00 2001 From: mun711 Date: Sat, 14 May 2022 11:55:26 +0300 Subject: [PATCH 05/14] Update Hibernate Search dev console card for multi-persistence-unit applications --- .../orm/runtime/PersistenceUnitUtil.java | 14 +++ .../HibernateOrmDevConsoleInfoSupplier.java | 20 +--- ...PersistenceUnitNameComparatorTestCase.java | 4 +- ...earchElasticsearchDevConsoleProcessor.java | 15 ++- .../resources/dev-templates/embedded.html | 5 +- .../resources/dev-templates/entity-types.html | 26 +++-- .../HibernateSearchDevConsoleRecorder.java | 34 +++--- .../devconsole/HibernateSearchSupplier.java | 107 +++++++++++++++--- 8 files changed, 165 insertions(+), 60 deletions(-) diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceUnitUtil.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceUnitUtil.java index 8c0d3306e4711..d106f3be75d20 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceUnitUtil.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceUnitUtil.java @@ -1,5 +1,6 @@ package io.quarkus.hibernate.orm.runtime; +import java.util.Comparator; import java.util.Locale; import javax.enterprise.inject.Default; @@ -37,6 +38,19 @@ public static InjectableInstance extensionInstanceForPersistenceUnit(Clas new PersistenceUnitExtension.Literal(persistenceUnitName)); } + public static class PersistenceUnitNameComparator implements Comparator { + @Override + public int compare(String o1, String o2) { + if (DEFAULT_PERSISTENCE_UNIT_NAME.equals(o1)) { + return -1; + } else if (DEFAULT_PERSISTENCE_UNIT_NAME.equals(o2)) { + return +1; + } else { + return o1.compareTo(o2); + } + } + } + @Deprecated public static InjectableInstance legacySingleExtensionInstanceForPersistenceUnit(Class beanType, String persistenceUnitName) { diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/devconsole/HibernateOrmDevConsoleInfoSupplier.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/devconsole/HibernateOrmDevConsoleInfoSupplier.java index 70592fc418bf3..d50a1ab2c8d30 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/devconsole/HibernateOrmDevConsoleInfoSupplier.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/devconsole/HibernateOrmDevConsoleInfoSupplier.java @@ -5,7 +5,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.EnumSet; import java.util.List; import java.util.Map; @@ -24,9 +23,9 @@ import org.hibernate.tool.schema.spi.ScriptTargetOutput; import org.hibernate.tool.schema.spi.TargetDescriptor; -public class HibernateOrmDevConsoleInfoSupplier implements Supplier { +import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil; - private static final String DEFAULT = ""; +public class HibernateOrmDevConsoleInfoSupplier implements Supplier { public static final PersistenceUnitsInfo INSTANCE = new PersistenceUnitsInfo(); @@ -99,7 +98,7 @@ public PersistenceUnitsInfo get() { public static class PersistenceUnitsInfo { private final Map persistenceUnits = Collections - .synchronizedMap(new TreeMap<>(new PersistenceUnitNameComparator())); + .synchronizedMap(new TreeMap<>(new PersistenceUnitUtil.PersistenceUnitNameComparator())); public Collection getPersistenceUnits() { return persistenceUnits.values(); @@ -229,17 +228,4 @@ public String getType() { } } - - static class PersistenceUnitNameComparator implements Comparator { - @Override - public int compare(String o1, String o2) { - if (DEFAULT.equals(o1)) { - return -1; - } else if (DEFAULT.equals(o2)) { - return +1; - } else { - return o1.compareTo(o2); - } - } - } } diff --git a/extensions/hibernate-orm/runtime/src/test/java/io/quarkus/hibernate/orm/runtime/devconsole/PersistenceUnitNameComparatorTestCase.java b/extensions/hibernate-orm/runtime/src/test/java/io/quarkus/hibernate/orm/runtime/devconsole/PersistenceUnitNameComparatorTestCase.java index 00a0b9b651826..7dfbe219d7978 100644 --- a/extensions/hibernate-orm/runtime/src/test/java/io/quarkus/hibernate/orm/runtime/devconsole/PersistenceUnitNameComparatorTestCase.java +++ b/extensions/hibernate-orm/runtime/src/test/java/io/quarkus/hibernate/orm/runtime/devconsole/PersistenceUnitNameComparatorTestCase.java @@ -7,6 +7,8 @@ import org.junit.jupiter.api.Test; +import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil; + public class PersistenceUnitNameComparatorTestCase { @Test @@ -16,7 +18,7 @@ public void puNameComparatorTest() { names.add("alpha"); names.add(""); names.add("beta"); - names.sort(new HibernateOrmDevConsoleInfoSupplier.PersistenceUnitNameComparator()); + names.sort(new PersistenceUnitUtil.PersistenceUnitNameComparator()); assertThat(names.get(0)).isEqualTo(""); assertThat(names.get(1)).isEqualTo("alpha"); diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/devconsole/HibernateSearchElasticsearchDevConsoleProcessor.java b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/devconsole/HibernateSearchElasticsearchDevConsoleProcessor.java index 416d65953587a..92246810a801d 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/devconsole/HibernateSearchElasticsearchDevConsoleProcessor.java +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/devconsole/HibernateSearchElasticsearchDevConsoleProcessor.java @@ -3,12 +3,17 @@ import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT; import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + import io.quarkus.deployment.IsDevelopment; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; import io.quarkus.devconsole.spi.DevConsoleRouteBuildItem; import io.quarkus.devconsole.spi.DevConsoleRuntimeTemplateInfoBuildItem; +import io.quarkus.hibernate.search.orm.elasticsearch.HibernateSearchElasticsearchPersistenceUnitConfiguredBuildItem; import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchRuntimeConfig; import io.quarkus.hibernate.search.orm.elasticsearch.runtime.devconsole.HibernateSearchDevConsoleRecorder; @@ -18,9 +23,13 @@ public class HibernateSearchElasticsearchDevConsoleProcessor { @Record(RUNTIME_INIT) public DevConsoleRuntimeTemplateInfoBuildItem collectBeanInfo(HibernateSearchDevConsoleRecorder recorder, HibernateSearchElasticsearchRuntimeConfig runtimeConfig, - CurateOutcomeBuildItem curateOutcomeBuildItem) { - return new DevConsoleRuntimeTemplateInfoBuildItem("indexedEntityTypes", - recorder.infoSupplier(runtimeConfig), this.getClass(), curateOutcomeBuildItem); + CurateOutcomeBuildItem curateOutcomeBuildItem, + List peristenceUnitBuildItems) { + Set persistenceUnitNames = peristenceUnitBuildItems.stream() + .map(HibernateSearchElasticsearchPersistenceUnitConfiguredBuildItem::getPersistenceUnitName) + .collect(Collectors.toSet()); + return new DevConsoleRuntimeTemplateInfoBuildItem("indexedPersistenceUnits", + recorder.infoSupplier(runtimeConfig, persistenceUnitNames), this.getClass(), curateOutcomeBuildItem); } @BuildStep diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/resources/dev-templates/embedded.html b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/resources/dev-templates/embedded.html index 10c92d629e123..9cc1a072f0350 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/resources/dev-templates/embedded.html +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/resources/dev-templates/embedded.html @@ -1,4 +1,7 @@ + + + Persistence units {info:indexedPersistenceUnits.persistenceUnits.size} - Indexed entity types {info:indexedEntityTypes.size()} + Indexed Entities {info:indexedPersistenceUnits.numberOfIndexedEntities}
diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/resources/dev-templates/entity-types.html b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/resources/dev-templates/entity-types.html index 079f27d7783c2..fd6cdcbffbff0 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/resources/dev-templates/entity-types.html +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/resources/dev-templates/entity-types.html @@ -1,11 +1,14 @@ {#include main} {#title}Indexed Entities{/title} {#body} -{#if info:indexedEntityTypes.isEmpty} +{#if info:indexedPersistenceUnits.persistenceUnits.isEmpty}

No indexed entities were found.

{#else}
- + +
+ {#for indexedPersistenceUnit in info:indexedPersistenceUnits.persistenceUnits} +

Persistence Unit {indexedPersistenceUnit.persistenceUnitName}

@@ -16,7 +19,7 @@ @@ -24,11 +27,12 @@ - {#for indexedEntityType in info:indexedEntityTypes} + {#for indexedEntityType in indexedPersistenceUnit.indexedEntities} @@ -37,15 +41,13 @@ {/for}
- +
Entity name
- +
{indexedEntityType.jpaName}
+ {/for}
{/if} {/body} diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/devconsole/HibernateSearchDevConsoleRecorder.java b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/devconsole/HibernateSearchDevConsoleRecorder.java index 1a7d66cd2554b..deff448a12de3 100644 --- a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/devconsole/HibernateSearchDevConsoleRecorder.java +++ b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/devconsole/HibernateSearchDevConsoleRecorder.java @@ -2,6 +2,8 @@ import java.time.Duration; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -19,9 +21,9 @@ @Recorder public class HibernateSearchDevConsoleRecorder { - public Supplier> infoSupplier( - HibernateSearchElasticsearchRuntimeConfig runtimeConfig) { - return new HibernateSearchSupplier(runtimeConfig); + public Supplier infoSupplier( + HibernateSearchElasticsearchRuntimeConfig runtimeConfig, Set persistenceUnitNames) { + return new HibernateSearchSupplier(runtimeConfig, persistenceUnitNames); } public Handler indexEntity() { @@ -31,19 +33,25 @@ protected void handlePostAsync(RoutingContext event, MultiMap form) throws Excep if (form.isEmpty()) { return; } - SearchMapping mapping = HibernateSearchSupplier.searchMapping(); - if (mapping == null) { + Set persitenceUnitNames = form.entries().stream().map(Map.Entry::getValue) + .collect(Collectors.toSet()); + Map mappings = HibernateSearchSupplier.searchMapping(persitenceUnitNames); + if (mappings.isEmpty()) { flashMessage(event, "There are no indexed entity types.", FlashScopeUtil.FlashMessageStatus.ERROR); return; } - mapping.scope(Object.class, - mapping.allIndexedEntities().stream() - .map(SearchIndexedEntity::jpaName) - .filter(form::contains) - .collect(Collectors.toList())) - .massIndexer() - .startAndWait(); - flashMessage(event, "Entities successfully reindexed", Duration.ofSeconds(10)); + for (Map.Entry entry : mappings.entrySet()) { + SearchMapping mapping = entry.getValue(); + List entityNames = mapping.allIndexedEntities().stream() + .map(SearchIndexedEntity::jpaName) + .filter(jpaName -> form.contains(jpaName, entry.getKey(), false)) + .collect(Collectors.toList()); + if (!entityNames.isEmpty()) { + mapping.scope(Object.class, entityNames).massIndexer() + .startAndWait(); + flashMessage(event, "Entities successfully reindexed", Duration.ofSeconds(10)); + } + } } }; } diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/devconsole/HibernateSearchSupplier.java b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/devconsole/HibernateSearchSupplier.java index 2460e905887c6..ef2b842263692 100644 --- a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/devconsole/HibernateSearchSupplier.java +++ b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/devconsole/HibernateSearchSupplier.java @@ -1,44 +1,125 @@ package io.quarkus.hibernate.search.orm.elasticsearch.runtime.devconsole; -import java.util.Collections; +import java.lang.annotation.Annotation; +import java.util.Arrays; +import java.util.Comparator; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; import java.util.function.Supplier; +import java.util.stream.Collector; import java.util.stream.Collectors; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.search.mapper.orm.entity.SearchIndexedEntity; import org.hibernate.search.mapper.orm.mapping.SearchMapping; import io.quarkus.arc.Arc; +import io.quarkus.hibernate.orm.PersistenceUnit; +import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil; import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchRuntimeConfig; -import io.quarkus.hibernate.search.orm.elasticsearch.runtime.devconsole.HibernateSearchSupplier.DevUiIndexedEntity; -public class HibernateSearchSupplier implements Supplier> { +public class HibernateSearchSupplier implements Supplier { private final HibernateSearchElasticsearchRuntimeConfig runtimeConfig; + private final Set persistenceUnitNames; - HibernateSearchSupplier(HibernateSearchElasticsearchRuntimeConfig runtimeConfig) { + HibernateSearchSupplier(HibernateSearchElasticsearchRuntimeConfig runtimeConfig, Set persistenceUnitNames) { this.runtimeConfig = runtimeConfig; + this.persistenceUnitNames = persistenceUnitNames; } @Override - public List get() { + public IndexedPersistenceUnits get() { if (!isEnabled()) { - return Collections.emptyList(); + return new IndexedPersistenceUnits(); } - SearchMapping mapping = searchMapping(); - if (mapping == null) { - return Collections.emptyList(); + Map mappings = searchMapping(persistenceUnitNames); + if (mappings.isEmpty()) { + return new IndexedPersistenceUnits(); } - return mapping.allIndexedEntities().stream().map(DevUiIndexedEntity::new).sorted() - .collect(Collectors.toList()); + return mappings.entrySet().stream() + .map(mapping -> new IndexedPersistenceUnit(mapping.getKey(), + mapping.getValue().allIndexedEntities().stream().map(DevUiIndexedEntity::new).sorted() + .collect(Collectors.toList()))) + .collect(Collector.of(IndexedPersistenceUnits::new, IndexedPersistenceUnits::add, + (left, right) -> { + left.addAll(right); + return left; + })); } private boolean isEnabled() { return runtimeConfig.defaultPersistenceUnit.enabled; } - public static SearchMapping searchMapping() { - return Arc.container().instance(SearchMapping.class).get(); + public static Map searchMapping(Set persistenceUnitNames) { + return Arrays.stream(getPersistenceUnitQualifiers(persistenceUnitNames)).map( + qualifier -> Arc.container().select(SearchMapping.class, qualifier).get()) + .collect(Collectors.toMap(HibernateSearchSupplier::getPersistenceUnitName, mapping -> mapping)); + } + + private static Annotation[] getPersistenceUnitQualifiers(Set persistenceUnitNames) { + return persistenceUnitNames.stream().map(PersistenceUnit.PersistenceUnitLiteral::new).toArray(Annotation[]::new); + } + + private static String getPersistenceUnitName(SearchMapping searchMapping) { + SessionFactoryImplementor sessionFactory = searchMapping.toOrmSessionFactory().unwrap(SessionFactoryImplementor.class); + String name = sessionFactory.getName(); + if (name != null) { + return name; + } + Object persistenceUnitName = sessionFactory.getProperties().get("hibernate.ejb.persistenceUnitName"); + if (persistenceUnitName != null) { + return persistenceUnitName.toString(); + } + return PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME; + } + + static class IndexedPersistenceUnits { + private final Set persistenceUnits = new TreeSet<>(new PersistenceUnitComparator()); + + public Set getPersistenceUnits() { + return persistenceUnits; + } + + public void add(IndexedPersistenceUnit indexedPersistenceUnit) { + persistenceUnits.add(indexedPersistenceUnit); + } + + public void addAll(IndexedPersistenceUnits right) { + persistenceUnits.addAll(right.persistenceUnits); + } + + public int getNumberOfIndexedEntities() { + return persistenceUnits.stream().mapToInt(pu -> pu.indexedEntities.size()).sum(); + } + } + + static class PersistenceUnitComparator implements Comparator { + Comparator persistenceUnitNameComparator = new PersistenceUnitUtil.PersistenceUnitNameComparator(); + + @Override + public int compare(IndexedPersistenceUnit o1, IndexedPersistenceUnit o2) { + return persistenceUnitNameComparator.compare(o1.persistenceUnitName, o2.persistenceUnitName); + } + } + + static class IndexedPersistenceUnit implements Comparable { + public final String persistenceUnitName; + + public final List indexedEntities; + + public IndexedPersistenceUnit(String persistenceUnitName, List indexedEntities) { + this.persistenceUnitName = persistenceUnitName; + this.indexedEntities = indexedEntities; + } + + @Override + public int compareTo(IndexedPersistenceUnit o) { + return this.persistenceUnitName.compareTo(o.persistenceUnitName); + } } public static class DevUiIndexedEntity implements Comparable { From 1b8d07512ae3b0c6c964dbabbff18d294af7616e Mon Sep 17 00:00:00 2001 From: Andy Damevin Date: Mon, 23 May 2022 15:08:48 +0200 Subject: [PATCH 06/14] Remove RegistrySnapshotCatalogCompatibilityTest --- ...est.java => CatalogCompatibilityTest.java} | 24 +++++-------------- .../LocalCatalogCompatibilityTest.java | 20 ---------------- 2 files changed, 6 insertions(+), 38 deletions(-) rename integration-tests/devtools/src/test/java/io/quarkus/platform/catalog/{RegistrySnapshotCatalogCompatibilityTest.java => CatalogCompatibilityTest.java} (81%) delete mode 100644 integration-tests/devtools/src/test/java/io/quarkus/platform/catalog/LocalCatalogCompatibilityTest.java diff --git a/integration-tests/devtools/src/test/java/io/quarkus/platform/catalog/RegistrySnapshotCatalogCompatibilityTest.java b/integration-tests/devtools/src/test/java/io/quarkus/platform/catalog/CatalogCompatibilityTest.java similarity index 81% rename from integration-tests/devtools/src/test/java/io/quarkus/platform/catalog/RegistrySnapshotCatalogCompatibilityTest.java rename to integration-tests/devtools/src/test/java/io/quarkus/platform/catalog/CatalogCompatibilityTest.java index 3d65b8f8f4aff..84149c7f2d24e 100644 --- a/integration-tests/devtools/src/test/java/io/quarkus/platform/catalog/RegistrySnapshotCatalogCompatibilityTest.java +++ b/integration-tests/devtools/src/test/java/io/quarkus/platform/catalog/CatalogCompatibilityTest.java @@ -5,17 +5,16 @@ import java.io.IOException; import java.util.List; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import io.quarkus.devtools.codestarts.quarkus.QuarkusCodestartCatalog; import io.quarkus.devtools.project.CodestartResourceLoadersBuilder; import io.quarkus.devtools.project.QuarkusProjectHelper; +import io.quarkus.devtools.testing.PlatformAwareTestBase; import io.quarkus.platform.catalog.processor.CatalogProcessor; import io.quarkus.platform.catalog.processor.ExtensionProcessor; import io.quarkus.platform.catalog.processor.ProcessedCategory; import io.quarkus.platform.descriptor.loader.json.ResourceLoader; -import io.quarkus.registry.Constants; import io.quarkus.registry.ExtensionCatalogResolver; import io.quarkus.registry.RegistryResolutionException; import io.quarkus.registry.catalog.Extension; @@ -25,20 +24,13 @@ import io.quarkus.registry.catalog.PlatformRelease; import io.quarkus.registry.catalog.PlatformStream; -@Disabled -public class RegistrySnapshotCatalogCompatibilityTest { - - private final ExtensionCatalogResolver catalogResolver = QuarkusProjectHelper.getCatalogResolver(); - - public RegistrySnapshotCatalogCompatibilityTest() throws RegistryResolutionException { - } +public class CatalogCompatibilityTest extends PlatformAwareTestBase { @Test - public void testRegistrySnapshotPlatformCatalog() throws RegistryResolutionException, IOException { - // TODO Use a local snapshot of the registry for testing - final PlatformCatalog platformCatalog = getRegistryPlatformCatalog(); - assertThat(platformCatalog.getMetadata().get(Constants.LAST_UPDATED)).isNotNull(); - testPlatformCatalog(catalogResolver, platformCatalog, "io.quarkus.platform"); + void testCatalog() throws RegistryResolutionException, IOException { + final ExtensionCatalogResolver catalogResolver = QuarkusProjectHelper.getCatalogResolver(); + testPlatformCatalog(catalogResolver, catalogResolver.resolvePlatformCatalog(), + "io.quarkus"); } static void testPlatformCatalog(ExtensionCatalogResolver catalogResolver, PlatformCatalog platformCatalog, @@ -55,7 +47,6 @@ static void testPlatformCatalog(ExtensionCatalogResolver catalogResolver, Platfo for (PlatformRelease r : s.getReleases()) { checkPlatformRelease(catalogResolver, r); } - } } @@ -102,7 +93,4 @@ private static void checkExtensionProcessor(Extension e) { extensionProcessor.getNonQuarkusBomOnly(); } - private PlatformCatalog getRegistryPlatformCatalog() throws RegistryResolutionException { - return catalogResolver.resolvePlatformCatalog(); - } } diff --git a/integration-tests/devtools/src/test/java/io/quarkus/platform/catalog/LocalCatalogCompatibilityTest.java b/integration-tests/devtools/src/test/java/io/quarkus/platform/catalog/LocalCatalogCompatibilityTest.java deleted file mode 100644 index ec7e505654782..0000000000000 --- a/integration-tests/devtools/src/test/java/io/quarkus/platform/catalog/LocalCatalogCompatibilityTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.quarkus.platform.catalog; - -import java.io.IOException; - -import org.junit.jupiter.api.Test; - -import io.quarkus.devtools.project.QuarkusProjectHelper; -import io.quarkus.devtools.testing.PlatformAwareTestBase; -import io.quarkus.registry.ExtensionCatalogResolver; -import io.quarkus.registry.RegistryResolutionException; - -public class LocalCatalogCompatibilityTest extends PlatformAwareTestBase { - - @Test - void testCatalog() throws RegistryResolutionException, IOException { - final ExtensionCatalogResolver catalogResolver = QuarkusProjectHelper.getCatalogResolver(); - RegistrySnapshotCatalogCompatibilityTest.testPlatformCatalog(catalogResolver, catalogResolver.resolvePlatformCatalog(), - "io.quarkus"); - } -} From 8135cfe666c6b3a08eb0af8bb7b54a0a2835ac91 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 23 May 2022 12:14:58 +0300 Subject: [PATCH 07/14] Provide details about failing MetaInfJandexReader Relates to: #25677 --- .../src/main/java/io/quarkus/deployment/index/IndexingUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/index/IndexingUtil.java b/core/deployment/src/main/java/io/quarkus/deployment/index/IndexingUtil.java index a6f2246150d5e..210d3cd3a0b24 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/index/IndexingUtil.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/index/IndexingUtil.java @@ -254,7 +254,7 @@ public Index apply(PathVisit visit) { "Can't read Jandex index from " + visit.getPath() + ": " + e.getMessage()); } } catch (IOException e) { - throw new UncheckedIOException(e); + throw new UncheckedIOException("Can't read Jandex index from " + visit.getPath(), e); } } } From ac42c79b3ac61939aaded4e8cf516ba4a9a69f1e Mon Sep 17 00:00:00 2001 From: Oleksiy Lukin Date: Fri, 20 May 2022 10:57:50 +0300 Subject: [PATCH 08/14] Podman usage with Quarkus article is added --- .../includes/devtools/prerequisites.adoc | 2 +- docs/src/main/asciidoc/podman.adoc | 75 +++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 docs/src/main/asciidoc/podman.adoc diff --git a/docs/src/main/asciidoc/includes/devtools/prerequisites.adoc b/docs/src/main/asciidoc/includes/devtools/prerequisites.adoc index c64c71b5f951d..d49cc9236379c 100644 --- a/docs/src/main/asciidoc/includes/devtools/prerequisites.adoc +++ b/docs/src/main/asciidoc/includes/devtools/prerequisites.adoc @@ -16,7 +16,7 @@ ifdef::prerequisites-docker[] * A working container runtime (Docker or Podman) endif::[] ifdef::prerequisites-docker-compose[] -* Docker and Docker Compose +* Docker and Docker Compose or xref:podman.adoc[Podman], and Docker Compose endif::[] ifndef::prerequisites-no-cli[] * Optionally the xref:cli-tooling.adoc[Quarkus CLI] if you want to use it diff --git a/docs/src/main/asciidoc/podman.adoc b/docs/src/main/asciidoc/podman.adoc new file mode 100644 index 0000000000000..06fbcd0f95010 --- /dev/null +++ b/docs/src/main/asciidoc/podman.adoc @@ -0,0 +1,75 @@ +//// +This guide is maintained in the main Quarkus repository +and pull requests should be submitted there: +https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc +//// += Using Podman with Quarkus + +https://podman.io/[Podman] is a daemonless and rootless container engine for developing, managing, and running OCI Containers on your Linux system or other OS. +Podman can be used the same way as Docker with the `podman-docker` package. + +== Installing Podman on Linux + +The Podman package is available in several Linux distributions. To install it for your OS, please refer to the https://podman.io/getting-started/installation[Podman installation guide]. +Below is the short installation instruction for popular Linux distributions: + +.Fedora +[source,bash] +---- +sudo dnf install podman podman-docker docker-compose +---- +.Ubuntu (21.04 and later) +[source,bash] +---- +sudo apt install podman podman-docker docker-compose +---- + +=== After installation + +Podman is a daemonless container engine. Most Quarkus Dev Services and Testcontainers expect a running Docker daemon listening at a Unix socket. +That's why the following steps are required. +[source,bash] +---- +# Enable the podman socket with Docker REST API +systemctl --user enable podman.socket --now +# Set the required envvars +export DOCKER_HOST=unix:///run/user/${UID}/podman/podman.sock +export TESTCONTAINERS_RYUK_DISABLED=true +---- +For a detailed explanation, see this https://quarkus.io/blog/quarkus-devservices-testcontainers-podman/[blog article]. + +To make changes permanent, add the exported environment variables to the "init" file from your shell. For example, the `bash` shell uses the `~/.bashrc` file: +[source,bash] +---- +echo "export DOCKER_HOST=unix:///run/user/${UID}/podman/podman.sock" >> ~/.bashrc +echo "export TESTCONTAINERS_RYUK_DISABLED=true" >> ~/.bashrc +---- + +=== Short names of images + +Testcontainers and Quarkus Dev Services also expect the container service they make requests against to be non-interactive. +In case you have multiple registries configured in your Docker or Podman configuration, and when using short image names, Podman responds with a prompt asking which registry should be used to pull images. + +We recommend you to avoid short names and always use fully specified names including the registry. +If, for some reason, it is not an option for you, you can disable this prompt by setting the `short-name-mode="disabled"` configuration property of Podman in `/etc/containers/registries.conf`. + +== Other operating systems + +Containers are really Linux. As such, Linux containers cannot run natively on macOS or Windows. +Therefore, the containers must run in a Linux virtual machine (VM), and a Podman client interacts with that VM. +So a native hypervisor subsystem and virtualization software is used to run the Linux VM on the OS, and then containers are run within this VM. + +.macOS +macOS users can install Podman through https://brew.sh/[Homebrew]. Once you have set up `brew`, you can use the `brew install` command to install Podman and `docker-compose`: +[source,bash] +---- +brew install podman +brew install docker-compose +podman machine init +podman machine start +alias docker='podman' +---- +For more details, please see the https://podman.io/getting-started/installation#macos[official Podman documentation] and this https://www.redhat.com/sysadmin/replace-docker-podman-macos[article]. + +.Windows +Please see the https://github.com/containers/podman/blob/main/docs/tutorials/podman-for-windows.md[Podman for Windows guide] for setup and usage instructions. \ No newline at end of file From 5ab1a215e26d58bbac5cdb13016a23e2e7ae20c1 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Mon, 23 May 2022 17:12:05 +0200 Subject: [PATCH 09/14] Exclude jakarta.activation:jakarta.activation-api from Search extension We use the com.sun.activation impl that also contains the API. --- .../hibernate-search-orm-elasticsearch/runtime/pom.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/pom.xml b/extensions/hibernate-search-orm-elasticsearch/runtime/pom.xml index 1f48eef3c9b0b..e166cddc4de24 100644 --- a/extensions/hibernate-search-orm-elasticsearch/runtime/pom.xml +++ b/extensions/hibernate-search-orm-elasticsearch/runtime/pom.xml @@ -38,6 +38,14 @@ javax.persistence javax.persistence-api + + + jakarta.activation + jakarta.activation-api + From 94eb61026cdaa577200d71edf7187585ae40162c Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Mon, 23 May 2022 20:43:46 +0200 Subject: [PATCH 10/14] Switch to a PAT for the Jakarta push Pushing with the default token prevents other workflows from being triggered by the push. --- .github/workflows/jakarta-rewrite.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/jakarta-rewrite.yml b/.github/workflows/jakarta-rewrite.yml index 1ed7e6bd1e668..15dd7060ac114 100644 --- a/.github/workflows/jakarta-rewrite.yml +++ b/.github/workflows/jakarta-rewrite.yml @@ -37,7 +37,7 @@ jobs: - name: Push changes to jakarta-rewrite uses: ad-m/github-push-action@v0.6.0 with: - github_token: ${{ secrets.GITHUB_TOKEN }} + github_token: ${{ secrets.JAKARTA_PUSH_PAT }} force: true branch: jakarta-rewrite - name: Report status From 58371c902afc6dedf456ce39ed2c010bdfda25fc Mon Sep 17 00:00:00 2001 From: Guillaume Le Floch Date: Mon, 23 May 2022 22:20:24 +0200 Subject: [PATCH 11/14] Leverage Gradle build cache in integration tests --- .../gradle/.gradle/gradle.properties | 1 + ...ExtensionToSingleModuleKtsProjectTest.java | 2 +- ...AddExtensionToSingleModuleProjectTest.java | 2 +- .../gradle/AdditionalSourceSetsTest.java | 5 ++-- .../AnnotationProcessorMultiModuleTest.java | 2 +- .../AnnotationProcessorSimpleModuleTest.java | 4 +-- .../gradle/ApplicationConfigurationTest.java | 2 +- .../quarkus/gradle/BeanInTestSourcesTest.java | 2 +- .../java/io/quarkus/gradle/BuildResult.java | 5 ++++ .../gradle/CustomFileSystemProviderTest.java | 2 +- .../gradle/DependencyConstraintsTest.java | 2 +- .../gradle/DependencyResolutionTest.java | 2 +- .../gradle/InjectBeanFromTestConfigTest.java | 2 +- .../gradle/IntegrationTestBuildTest.java | 4 +-- .../quarkus/gradle/JandexMultiModuleTest.java | 4 +-- .../gradle/KotlinGRPCProjectBuildTest.java | 4 +-- .../MultiModuleKotlinProjectBuildTest.java | 4 +-- .../gradle/MultiSourceProjectTest.java | 2 +- .../gradle/QuarkusPluginFunctionalTest.java | 30 +++++++++---------- .../SpringDependencyManagementTest.java | 2 +- .../quarkus/gradle/TestFixtureModuleTest.java | 2 +- .../gradle/TestFixtureMultiModuleTest.java | 2 +- 22 files changed, 46 insertions(+), 41 deletions(-) create mode 100644 integration-tests/gradle/.gradle/gradle.properties diff --git a/integration-tests/gradle/.gradle/gradle.properties b/integration-tests/gradle/.gradle/gradle.properties new file mode 100644 index 0000000000000..5f1ed7bbe024a --- /dev/null +++ b/integration-tests/gradle/.gradle/gradle.properties @@ -0,0 +1 @@ +org.gradle.caching=true \ No newline at end of file diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/AddExtensionToSingleModuleKtsProjectTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/AddExtensionToSingleModuleKtsProjectTest.java index ab7ea55623e5d..b53b9f6b72f83 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/AddExtensionToSingleModuleKtsProjectTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/AddExtensionToSingleModuleKtsProjectTest.java @@ -40,7 +40,7 @@ public void testRemoveNonExistentExtension() throws IOException, URISyntaxExcept final File projectDir = getProjectDir("add-remove-extension-single-module-kts"); BuildResult buildResult = runGradleWrapper(projectDir, "clean", "build"); - assertThat(buildResult.getTasks().get(":test")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(buildResult.getTasks().get(":test"))).isTrue(); final Path buildKts = projectDir.toPath().resolve("build.gradle.kts"); assertThat(buildKts).exists(); diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/AddExtensionToSingleModuleProjectTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/AddExtensionToSingleModuleProjectTest.java index 9d6965d4547f6..3a163d5b5af34 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/AddExtensionToSingleModuleProjectTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/AddExtensionToSingleModuleProjectTest.java @@ -37,7 +37,7 @@ public void testRemoveNonExistentExtension() throws IOException, URISyntaxExcept final File projectDir = getProjectDir("add-remove-extension-single-module"); BuildResult buildResult = runGradleWrapper(projectDir, "clean", "build"); - assertThat(buildResult.getTasks().get(":test")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(buildResult.getTasks().get(":test"))).isTrue(); final Path build = projectDir.toPath().resolve("build.gradle"); assertThat(build).exists(); diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/AdditionalSourceSetsTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/AdditionalSourceSetsTest.java index e335167b4d681..66c16390f524f 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/AdditionalSourceSetsTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/AdditionalSourceSetsTest.java @@ -1,6 +1,6 @@ package io.quarkus.gradle; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; import java.io.File; import java.io.IOException; @@ -18,7 +18,6 @@ public class AdditionalSourceSetsTest extends QuarkusGradleWrapperTestBase { public void executeFunctionalTest() throws URISyntaxException, IOException, InterruptedException { final File projectDir = getProjectDir("additional-source-sets"); BuildResult result = runGradleWrapper(projectDir, "functionalTest"); - assertEquals(BuildResult.SUCCESS_OUTCOME, result.getTasks().get(":functionalTest"), - "Failed to run tests defined in an alternate test sources directory"); + assertThat(BuildResult.isSuccessful(result.getTasks().get(":functionalTest"))).isTrue(); } } diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/AnnotationProcessorMultiModuleTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/AnnotationProcessorMultiModuleTest.java index 3a43713c46cb5..156bccf71e914 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/AnnotationProcessorMultiModuleTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/AnnotationProcessorMultiModuleTest.java @@ -14,6 +14,6 @@ public void shouldRunTestCorrectly() throws Exception { BuildResult buildResult = runGradleWrapper(projectDir, "clean", "test"); - assertThat(buildResult.getTasks().get(":application:test")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(buildResult.getTasks().get(":application:test"))).isTrue(); } } diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/AnnotationProcessorSimpleModuleTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/AnnotationProcessorSimpleModuleTest.java index 66a61bbc6b048..07445b9415f88 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/AnnotationProcessorSimpleModuleTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/AnnotationProcessorSimpleModuleTest.java @@ -15,7 +15,7 @@ public void shouldRunTestCorrectly() throws Exception { BuildResult buildResult = runGradleWrapper(projectDir, "clean", "test"); - assertThat(buildResult.getTasks().get(":test")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(buildResult.getTasks().get(":test"))).isTrue(); } @Test @@ -24,7 +24,7 @@ public void shouldContainsPanacheMarkerFile() throws Exception { BuildResult buildResult = runGradleWrapper(projectDir, "clean", "quarkusBuild"); - assertThat(buildResult.getTasks().get(":quarkusBuild")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(buildResult.getTasks().get(":quarkusBuild"))).isTrue(); File buildDir = new File(projectDir, "build"); Path metaInfDir = buildDir.toPath().resolve("classes").resolve("java").resolve("main").resolve("META-INF"); diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/ApplicationConfigurationTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/ApplicationConfigurationTest.java index f44453718537c..86def66f466ea 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/ApplicationConfigurationTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/ApplicationConfigurationTest.java @@ -14,7 +14,7 @@ public void shouldSuccessfullyInjectApplicationConfigInTest() throws Exception { BuildResult testResult = runGradleWrapper(projectDir, "clean", ":application:test"); - assertThat(testResult.getTasks().get(":application:test")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(testResult.getTasks().get(":application:test"))).isTrue(); } } diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/BeanInTestSourcesTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/BeanInTestSourcesTest.java index 374e98927150a..6e68885d1a152 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/BeanInTestSourcesTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/BeanInTestSourcesTest.java @@ -12,6 +12,6 @@ public class BeanInTestSourcesTest extends QuarkusGradleWrapperTestBase { public void testBasicMultiModuleBuild() throws Exception { final File projectDir = getProjectDir("bean-in-testsources-project"); final BuildResult build = runGradleWrapper(projectDir, "clean", "test"); - assertThat(build.getTasks().get(":test")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(build.getTasks().get(":test"))).isTrue(); } } diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/BuildResult.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/BuildResult.java index df3e5e432f49d..c1288e1e04cf3 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/BuildResult.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/BuildResult.java @@ -12,6 +12,7 @@ public class BuildResult { public static final String SUCCESS_OUTCOME = "SUCCESS"; public static final String UPTODATE_OUTCOME = "UP-TO-DATE"; + public static final String FROM_CACHE = "FROM-CACHE"; private static final String TASK_RESULT_PREFIX = "> Task"; private Map tasks; @@ -54,4 +55,8 @@ public Map getTasks() { public String getOutput() { return output; } + + public static boolean isSuccessful(String result) { + return SUCCESS_OUTCOME.equals(result) || FROM_CACHE.equals(result); + } } diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/CustomFileSystemProviderTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/CustomFileSystemProviderTest.java index 983a91b3d7ca0..266da97dce703 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/CustomFileSystemProviderTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/CustomFileSystemProviderTest.java @@ -14,6 +14,6 @@ public void test() throws Exception { final File projectDir = getProjectDir("custom-filesystem-provider"); BuildResult build = runGradleWrapper(projectDir, "clean", ":application:test"); - assertThat(build.getTasks().get(":application:test")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(build.getTasks().get(":application:test"))).isTrue(); } } diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/DependencyConstraintsTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/DependencyConstraintsTest.java index a8070a019be23..366d71eaaca94 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/DependencyConstraintsTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/DependencyConstraintsTest.java @@ -15,7 +15,7 @@ public void shoudBuildProjectWithDependencyConstraint() throws Exception { BuildResult buildResult = runGradleWrapper(projectDir, "clean", "quarkusBuild", "-Dquarkus.package.type=mutable-jar"); - assertThat(buildResult.getTasks().get(":quarkusBuild")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(buildResult.getTasks().get(":quarkusBuild"))).isTrue(); final File buildDir = new File(projectDir, "build"); final Path mainLib = buildDir.toPath().resolve("quarkus-app").resolve("lib").resolve("main"); diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/DependencyResolutionTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/DependencyResolutionTest.java index 509cfb6d3d0fc..de98f99ff7c94 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/DependencyResolutionTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/DependencyResolutionTest.java @@ -16,6 +16,6 @@ public void shouldResolveDependencyVersionFromSuperConfigurationProject() final BuildResult result = runGradleWrapper(projectDir, "clean", "quarkusBuild"); - assertThat(result.getTasks().get(":quarkusBuild")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(result.getTasks().get(":quarkusBuild"))).isTrue(); } } diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/InjectBeanFromTestConfigTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/InjectBeanFromTestConfigTest.java index b052e66a3f05c..69f233f720df1 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/InjectBeanFromTestConfigTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/InjectBeanFromTestConfigTest.java @@ -14,6 +14,6 @@ public void testBasicMultiModuleBuild() throws Exception { final File projectDir = getProjectDir("inject-bean-from-test-config"); BuildResult build = runGradleWrapper(projectDir, "clean", ":application:test"); - assertThat(build.getTasks().get(":application:test")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(build.getTasks().get(":application:test"))).isTrue(); } } diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/IntegrationTestBuildTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/IntegrationTestBuildTest.java index 1145c1a07f1c7..60c482b0f68cc 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/IntegrationTestBuildTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/IntegrationTestBuildTest.java @@ -14,8 +14,8 @@ public void shouldRunIntegrationTestAsPartOfBuild() throws Exception { BuildResult buildResult = runGradleWrapper(projectDir, "clean", "quarkusIntTest"); - assertThat(buildResult.getTasks().get(":test")).isEqualTo(BuildResult.SUCCESS_OUTCOME); - assertThat(buildResult.getTasks().get(":quarkusIntTest")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(buildResult.getTasks().get(":test"))).isTrue(); + assertThat(BuildResult.isSuccessful(buildResult.getTasks().get(":quarkusIntTest"))).isTrue(); } } diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/JandexMultiModuleTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/JandexMultiModuleTest.java index f27fe605878b6..d05f5530edf70 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/JandexMultiModuleTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/JandexMultiModuleTest.java @@ -14,8 +14,8 @@ public void testBasicMultiModuleBuild() throws Exception { final File projectDir = getProjectDir("jandex-basic-multi-module-project"); BuildResult build = runGradleWrapper(projectDir, "clean", ":application:quarkusBuild"); - assertThat(build.getTasks().get(":common:jandex")).isEqualTo(BuildResult.SUCCESS_OUTCOME); - assertThat(build.getTasks().get(":application:quarkusBuild")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(build.getTasks().get(":common:jandex"))).isTrue(); + assertThat(BuildResult.isSuccessful(build.getTasks().get(":application:quarkusBuild"))).isTrue(); } } diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/KotlinGRPCProjectBuildTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/KotlinGRPCProjectBuildTest.java index 1148f70087ca1..7651be57e8496 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/KotlinGRPCProjectBuildTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/KotlinGRPCProjectBuildTest.java @@ -12,7 +12,7 @@ public class KotlinGRPCProjectBuildTest extends QuarkusGradleWrapperTestBase { public void testBasicMultiModuleBuild() throws Exception { final File projectDir = getProjectDir("kotlin-grpc-project"); final BuildResult build = runGradleWrapper(projectDir, "clean", "build"); - assertThat(build.getTasks().get(":quarkusGenerateCode")).isEqualTo(BuildResult.SUCCESS_OUTCOME); - assertThat(build.getTasks().get(":compileKotlin")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(build.getTasks().get(":quarkusGenerateCode"))).isTrue(); + assertThat(BuildResult.isSuccessful(build.getTasks().get(":compileKotlin"))).isTrue(); } } diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/MultiModuleKotlinProjectBuildTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/MultiModuleKotlinProjectBuildTest.java index 020037329c481..ecb3fb0cd3d48 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/MultiModuleKotlinProjectBuildTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/MultiModuleKotlinProjectBuildTest.java @@ -12,7 +12,7 @@ public class MultiModuleKotlinProjectBuildTest extends QuarkusGradleWrapperTestB public void testBasicMultiModuleBuild() throws Exception { final File projectDir = getProjectDir("multi-module-kotlin-project"); final BuildResult build = runGradleWrapper(projectDir, "clean", "build"); - assertThat(build.getTasks().get(":quarkusGenerateCode")).isEqualTo(BuildResult.SUCCESS_OUTCOME); - assertThat(build.getTasks().get(":compileKotlin")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(build.getTasks().get(":quarkusGenerateCode"))).isTrue(); + assertThat(BuildResult.isSuccessful(build.getTasks().get(":compileKotlin"))).isTrue(); } } diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/MultiSourceProjectTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/MultiSourceProjectTest.java index 640b8569b9abb..bd73097714714 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/MultiSourceProjectTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/MultiSourceProjectTest.java @@ -13,7 +13,7 @@ public void shouldRunTest() throws Exception { final File projectDir = getProjectDir("multi-source-project"); final BuildResult buildResult = runGradleWrapper(projectDir, ":clean", ":test"); - assertThat(buildResult.getTasks().get(":test")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(buildResult.getTasks().get(":test"))).isTrue(); } } diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java index fa25471402d78..686a25c1480cd 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java @@ -38,7 +38,7 @@ public void canBuild(SourceType sourceType) throws Exception { BuildResult build = runGradleWrapper(projectRoot, "build", "--stacktrace"); - assertThat(build.getTasks().get(":build")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(build.getTasks().get(":build"))).isTrue(); // gradle build should not build the native image assertThat(build.getTasks().get(":buildNative")).isNull(); Path buildDir = projectRoot.toPath().resolve("build"); @@ -51,7 +51,7 @@ public void canDetectUpToDateBuild() throws Exception { createProject(SourceType.JAVA); BuildResult firstBuild = runGradleWrapper(projectRoot, "quarkusBuild", "--stacktrace"); - assertThat(firstBuild.getTasks().get(":quarkusBuild")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(firstBuild.getTasks().get(":quarkusBuild"))).isTrue(); BuildResult secondBuild = runGradleWrapper(projectRoot, "quarkusBuild", "--stacktrace"); assertThat(secondBuild.getTasks().get(":quarkusBuild")).isEqualTo(BuildResult.UPTODATE_OUTCOME); @@ -62,13 +62,13 @@ public void canDetectResourceChangeWhenBuilding() throws Exception { createProject(SourceType.JAVA); BuildResult firstBuild = runGradleWrapper(projectRoot, "quarkusBuild", "--stacktrace"); - assertThat(firstBuild.getTasks().get(":quarkusBuild")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(firstBuild.getTasks().get(":quarkusBuild"))).isTrue(); final File applicationProperties = projectRoot.toPath().resolve("src/main/resources/application.properties").toFile(); Files.write(applicationProperties.toPath(), "quarkus.http.port=8888".getBytes()); BuildResult secondBuild = runGradleWrapper(projectRoot, "quarkusBuild", "--stacktrace"); - assertThat(secondBuild.getTasks().get(":quarkusBuild")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(secondBuild.getTasks().get(":quarkusBuild"))).isTrue(); } @Test @@ -76,14 +76,14 @@ public void canDetectClassChangeWhenBuilding() throws Exception { createProject(SourceType.JAVA); BuildResult firstBuild = runGradleWrapper(projectRoot, "quarkusBuild", "--stacktrace"); - assertThat(firstBuild.getTasks().get(":quarkusBuild")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(firstBuild.getTasks().get(":quarkusBuild"))).isTrue(); final File greetingResourceFile = projectRoot.toPath().resolve("src/main/java/org/acme/foo/GreetingResource.java") .toFile(); DevModeTestUtils.filter(greetingResourceFile, ImmutableMap.of("\"/greeting\"", "\"/test/hello\"")); BuildResult secondBuild = runGradleWrapper(projectRoot, "quarkusBuild", "--stacktrace"); - assertThat(secondBuild.getTasks().get(":quarkusBuild")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(secondBuild.getTasks().get(":quarkusBuild"))).isTrue(); } @Test @@ -91,11 +91,11 @@ public void canDetectClasspathChangeWhenBuilding() throws Exception { createProject(SourceType.JAVA); BuildResult firstBuild = runGradleWrapper(projectRoot, "quarkusBuild", "--stacktrace"); - assertThat(firstBuild.getTasks().get(":quarkusBuild")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(firstBuild.getTasks().get(":quarkusBuild"))).isTrue(); runGradleWrapper(projectRoot, "addExtension", "--extensions=hibernate-orm"); BuildResult secondBuild = runGradleWrapper(projectRoot, "quarkusBuild", "--stacktrace"); - assertThat(secondBuild.getTasks().get(":quarkusBuild")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(secondBuild.getTasks().get(":quarkusBuild"))).isTrue(); } @Test @@ -104,13 +104,13 @@ public void canDetectOutputChangeWhenBuilding() throws Exception { BuildResult firstBuild = runGradleWrapper(projectRoot, "quarkusBuild", "--stacktrace"); - assertThat(firstBuild.getTasks().get(":quarkusBuild")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(firstBuild.getTasks().get(":quarkusBuild"))).isTrue(); Path runnerJar = projectRoot.toPath().resolve("build").resolve("quarkus-app").resolve("quarkus-run.jar"); Files.delete(runnerJar); BuildResult secondBuild = runGradleWrapper(projectRoot, "quarkusBuild", "--stacktrace"); - assertThat(secondBuild.getTasks().get(":quarkusBuild")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(secondBuild.getTasks().get(":quarkusBuild"))).isTrue(); assertThat(runnerJar).exists(); } @@ -120,7 +120,7 @@ public void canDetectUpToDateTests() throws Exception { BuildResult firstBuild = runGradleWrapper(projectRoot, "test"); - assertThat(firstBuild.getTasks().get(":test")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(firstBuild.getTasks().get(":test"))).isTrue(); BuildResult secondBuild = runGradleWrapper(projectRoot, "test"); @@ -133,12 +133,12 @@ public void canDetectSystemPropertyChangeWhenBuilding() throws Exception { BuildResult firstBuild = runGradleWrapper(projectRoot, "quarkusBuild", "--stacktrace"); - assertThat(firstBuild.getTasks().get(":quarkusBuild")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(firstBuild.getTasks().get(":quarkusBuild"))).isTrue(); assertThat(projectRoot.toPath().resolve("build").resolve("quarkus-app").resolve("quarkus-run.jar")).exists(); BuildResult secondBuild = runGradleWrapper(projectRoot, "quarkusBuild", "-Dquarkus.package.type=fast-jar"); - assertThat(secondBuild.getTasks().get(":quarkusBuild")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(secondBuild.getTasks().get(":quarkusBuild"))).isTrue(); assertThat(projectRoot.toPath().resolve("build").resolve("quarkus-app")).exists(); } @@ -148,7 +148,7 @@ public void canRunTest() throws Exception { BuildResult buildResult = runGradleWrapper(projectRoot, "test", "--stacktrace"); - assertThat(buildResult.getTasks().get(":test")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(buildResult.getTasks().get(":test"))).isTrue(); } @Test @@ -158,7 +158,7 @@ public void generateCodeBeforeTests() throws Exception { BuildResult firstBuild = runGradleWrapper(projectRoot, "test", "--stacktrace"); assertThat(firstBuild.getOutput()).contains("Task :quarkusGenerateCode"); assertThat(firstBuild.getOutput()).contains("Task :quarkusGenerateCodeTests"); - assertThat(firstBuild.getTasks().get(":test")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(firstBuild.getTasks().get(":test"))).isTrue(); } private void createProject(SourceType sourceType) throws Exception { diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/SpringDependencyManagementTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/SpringDependencyManagementTest.java index b36c10a82019b..36ff0a9dee4b8 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/SpringDependencyManagementTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/SpringDependencyManagementTest.java @@ -17,6 +17,6 @@ public void testQuarkusBuildShouldWorkWithSpringDependencyManagement() final BuildResult result = runGradleWrapper(projectDir, "clean", "quarkusBuild"); - assertThat(result.getTasks().get(":quarkusBuild")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(result.getTasks().get(":quarkusBuild"))).isTrue(); } } diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/TestFixtureModuleTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/TestFixtureModuleTest.java index 2b60e9226ef1a..92f0a90f36350 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/TestFixtureModuleTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/TestFixtureModuleTest.java @@ -16,6 +16,6 @@ public void testTaskShouldUseTestFixtures() throws IOException, URISyntaxExcepti final BuildResult result = runGradleWrapper(projectDir, "clean", "test"); - assertThat(result.getTasks().get(":test")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(result.getTasks().get(":test"))).isTrue(); } } diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/TestFixtureMultiModuleTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/TestFixtureMultiModuleTest.java index 00d9076bdca26..b48cbfda403ff 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/TestFixtureMultiModuleTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/TestFixtureMultiModuleTest.java @@ -14,6 +14,6 @@ public class TestFixtureMultiModuleTest extends QuarkusGradleWrapperTestBase { public void testTaskShouldUseTestFixtures() throws IOException, URISyntaxException, InterruptedException { final File projectDir = getProjectDir("test-fixtures-multi-module"); final BuildResult result = runGradleWrapper(projectDir, "clean", "test"); - assertThat(result.getTasks().get(":application:test")).isEqualTo(BuildResult.SUCCESS_OUTCOME); + assertThat(BuildResult.isSuccessful(result.getTasks().get(":application:test"))).isTrue(); } } From eca2df863f84379afafd59c9ceb5b13896a448d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferraz=20Campos=20Florentino?= Date: Mon, 23 May 2022 20:54:33 -0300 Subject: [PATCH 12/14] Changing "internral" by "internal" --- docs/src/main/asciidoc/deploying-to-openshift.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/deploying-to-openshift.adoc b/docs/src/main/asciidoc/deploying-to-openshift.adoc index b9f7033128284..a96b5fe5f0274 100644 --- a/docs/src/main/asciidoc/deploying-to-openshift.adoc +++ b/docs/src/main/asciidoc/deploying-to-openshift.adoc @@ -361,7 +361,7 @@ quarkus.openshift.deployment-kind=Deployment ---- Since `Deployment` is a Kubernetes resource and not Openshift specific, it can't possibly leverage `ImageStream` resources, as is the case with `DeploymentConfig`. This means that the image references need to include the container image registry that hosts the image. -When the image is built, using Openshift builds (s2i binary and docker strategy) the Openshift internral image registry `image-registry.openshift-image-registry.svc:5000` will be used, unless an other registry has been explicitly specified by the user. Please note, that in the internal registry the project/namespace name is added as part of the image repository: `image-registry.openshift-image-registry.svc:5000//:`, so users will need to make sure that the target project/namespace name is aligned with the `quarkus.container-image.group`. +When the image is built, using Openshift builds (s2i binary and docker strategy) the Openshift internal image registry `image-registry.openshift-image-registry.svc:5000` will be used, unless an other registry has been explicitly specified by the user. Please note, that in the internal registry the project/namespace name is added as part of the image repository: `image-registry.openshift-image-registry.svc:5000//:`, so users will need to make sure that the target project/namespace name is aligned with the `quarkus.container-image.group`. [source,properties] ---- From d6a5178f109d1adf1368da1cdd30b524378b8f66 Mon Sep 17 00:00:00 2001 From: Jose Date: Tue, 24 May 2022 07:58:37 +0200 Subject: [PATCH 13/14] Resteasy Reactive: Fix support media types with suffixes Given a media type with suffix, for example: "application/test+json". Before these changes, Resteasy Reactive was trying to find the writer/reader using only the suffix. For example, with a media type "application/test+json", it would only use "json" to locate the right writer/reader. Spite of this has been working well so far, if users provide a custom writer/reader for a concrete media type with suffix: ```java @Provider @Produces("text/test+suffix") public static class SuffixMessageBodyWriter implements ServerMessageBodyWriter { @Override public boolean isWriteable(Class type, Type genericType, ResteasyReactiveResourceInfo target, MediaType mediaType) { // ... } @Override public void writeResponse(Object o, Type genericType, ServerRequestContext context) { // ... } // ... } ``` The above writer will never be used. After these changes, we will use the media type with suffix, plus the split parts of the suffix. For example, if the media type with suffix is: "application/test+json", it will try to find the best writer/reader for "application/test+json", "application/test" and then "application/json" (In this order). Fix https://github.com/quarkusio/quarkus/issues/15982 --- .../MediaTypesWithSuffixHandlingTest.java | 216 ++++++++++++++++++ .../ClientReaderInterceptorContextImpl.java | 3 +- .../reactive/common/core/Serialisers.java | 51 +++-- .../reactive/common/model/ResourceReader.java | 14 +- .../reactive/common/model/ResourceWriter.java | 20 +- .../reactive/common/util/MediaTypeHelper.java | 87 ++++++- .../reactive/common/util/ServerMediaType.java | 11 +- .../serialization/DynamicEntityWriter.java | 3 +- .../startup/RuntimeResourceDeployment.java | 5 +- .../server/handlers/MediaTypeMapper.java | 5 +- .../handlers/RequestDeserializeHandler.java | 3 +- 11 files changed, 363 insertions(+), 55 deletions(-) create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/MediaTypesWithSuffixHandlingTest.java diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/MediaTypesWithSuffixHandlingTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/MediaTypesWithSuffixHandlingTest.java new file mode 100644 index 0000000000000..4b741ca17e064 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/MediaTypesWithSuffixHandlingTest.java @@ -0,0 +1,216 @@ +package io.quarkus.resteasy.reactive.server.test.resource.basic; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.Provider; + +import org.hamcrest.Matchers; +import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveResourceInfo; +import org.jboss.resteasy.reactive.server.spi.ServerMessageBodyReader; +import org.jboss.resteasy.reactive.server.spi.ServerMessageBodyWriter; +import org.jboss.resteasy.reactive.server.spi.ServerRequestContext; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +public class MediaTypesWithSuffixHandlingTest { + + @RegisterExtension + static QuarkusUnitTest testExtension = new QuarkusUnitTest() + .setArchiveProducer(() -> { + JavaArchive archive = ShrinkWrap.create(JavaArchive.class); + archive.addClasses(TestResource.class, NoSuffixMessageBodyWriter.class, SuffixMessageBodyWriter.class); + return archive; + }); + + @Test + public void testWriterWithoutSuffix() { + RestAssured.get("/test/writer/with-no-suffix") + .then() + .statusCode(200) + .body(Matchers.equalTo("result - no suffix writer")); + } + + @Test + public void testReaderWithoutSuffix() { + RestAssured.get("/test/reader/with-no-suffix") + .then() + .statusCode(200) + .body(Matchers.equalTo("from reader - result")); + } + + @Test + public void testWriterWithSuffix() { + RestAssured.get("/test/writer/with-suffix") + .then() + .statusCode(200) + .body(Matchers.equalTo("result - suffix writer")); + } + + @Test + public void testReaderWithSuffix() { + RestAssured.get("/test/reader/with-suffix") + .then() + .statusCode(200) + .body(Matchers.equalTo("from reader suffix - result")); + } + + @Path("/test") + public static class TestResource { + + @GET + @Path("/writer/with-no-suffix") + @Produces("text/test") + public String writerSimple() { + return "result"; + } + + @GET + @Path("/writer/with-suffix") + @Produces("text/test+suffix") + public String writerSuffix() { + return "result"; + } + + @GET + @Path("/reader/with-no-suffix") + @Consumes("text/test") + public String readerSimple(Object fromReader) { + return fromReader + " - result"; + } + + @GET + @Path("/reader/with-suffix") + @Consumes("text/test+suffix") + public String readerSuffix(Object fromReader) { + return fromReader + " - result"; + } + } + + @Provider + @Consumes("text/test") + @Produces("text/test") + public static class NoSuffixMessageBodyWriter implements ServerMessageBodyWriter, ServerMessageBodyReader { + + @Override + public boolean isWriteable(Class type, Type genericType, ResteasyReactiveResourceInfo target, MediaType mediaType) { + return true; + } + + @Override + public void writeResponse(Object o, Type genericType, ServerRequestContext context) + throws WebApplicationException, IOException { + String response = (String) o; + response += " - no suffix writer"; + context.getOrCreateOutputStream().write(response.getBytes(StandardCharsets.UTF_8)); + } + + @Override + public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + throw new IllegalStateException("should never have been called"); + } + + @Override + public void writeTo(Object o, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, OutputStream entityStream) + throws IOException, WebApplicationException { + throw new IllegalStateException("should never have been called"); + } + + @Override + public boolean isReadable(Class type, Type genericType, ResteasyReactiveResourceInfo lazyMethod, + MediaType mediaType) { + return true; + } + + @Override + public Object readFrom(Class type, Type genericType, MediaType mediaType, + ServerRequestContext context) throws WebApplicationException { + return "from reader"; + } + + @Override + public boolean isReadable(Class aClass, Type type, Annotation[] annotations, MediaType mediaType) { + throw new IllegalStateException("should never have been called"); + } + + @Override + public Object readFrom(Class aClass, Type type, Annotation[] annotations, MediaType mediaType, + MultivaluedMap multivaluedMap, InputStream inputStream) + throws WebApplicationException { + throw new IllegalStateException("should never have been called"); + } + } + + @Provider + @Consumes("text/test+suffix") + @Produces("text/test+suffix") + public static class SuffixMessageBodyWriter implements ServerMessageBodyWriter, ServerMessageBodyReader { + + @Override + public boolean isWriteable(Class type, Type genericType, ResteasyReactiveResourceInfo target, MediaType mediaType) { + return true; + } + + @Override + public void writeResponse(Object o, Type genericType, ServerRequestContext context) + throws WebApplicationException, IOException { + String response = (String) o; + response += " - suffix writer"; + context.getOrCreateOutputStream().write(response.getBytes(StandardCharsets.UTF_8)); + } + + @Override + public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + throw new IllegalStateException("should never have been called"); + } + + @Override + public void writeTo(Object o, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, OutputStream entityStream) + throws IOException, WebApplicationException { + throw new IllegalStateException("should never have been called"); + } + + @Override + public boolean isReadable(Class type, Type genericType, ResteasyReactiveResourceInfo lazyMethod, + MediaType mediaType) { + return true; + } + + @Override + public Object readFrom(Class type, Type genericType, MediaType mediaType, + ServerRequestContext context) throws WebApplicationException { + return "from reader suffix"; + } + + @Override + public boolean isReadable(Class aClass, Type type, Annotation[] annotations, MediaType mediaType) { + throw new IllegalStateException("should never have been called"); + } + + @Override + public Object readFrom(Class aClass, Type type, Annotation[] annotations, MediaType mediaType, + MultivaluedMap multivaluedMap, InputStream inputStream) + throws WebApplicationException { + throw new IllegalStateException("should never have been called"); + } + } + +} diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientReaderInterceptorContextImpl.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientReaderInterceptorContextImpl.java index c73f288f0c87a..55815186fa959 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientReaderInterceptorContextImpl.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientReaderInterceptorContextImpl.java @@ -17,7 +17,6 @@ import org.jboss.resteasy.reactive.common.core.Serialisers; import org.jboss.resteasy.reactive.common.jaxrs.ConfigurationImpl; import org.jboss.resteasy.reactive.common.util.CaseInsensitiveMap; -import org.jboss.resteasy.reactive.common.util.MediaTypeHelper; public class ClientReaderInterceptorContextImpl extends AbstractClientInterceptorContextImpl implements ReaderInterceptorContext { @@ -35,7 +34,7 @@ public ClientReaderInterceptorContextImpl(Annotation[] annotations, Class ent Map properties, MultivaluedMap headers, ConfigurationImpl configuration, Serialisers serialisers, InputStream inputStream, ReaderInterceptor[] interceptors) { - super(annotations, entityClass, entityType, MediaTypeHelper.withSuffixAsSubtype(mediaType), properties); + super(annotations, entityClass, entityType, mediaType, properties); this.configuration = configuration; this.serialisers = serialisers; this.inputStream = inputStream; diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/Serialisers.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/Serialisers.java index 9a1c82d1795ba..e543e7cf976cb 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/Serialisers.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/Serialisers.java @@ -43,7 +43,7 @@ public List> findReaders(ConfigurationImpl configuration, C public List> findReaders(ConfigurationImpl configuration, Class entityType, MediaType mediaType, RuntimeType runtimeType) { - List mt = Collections.singletonList(mediaType); + List desired = MediaTypeHelper.getUngroupedMediaTypes(mediaType); List> ret = new ArrayList<>(); Deque> toProcess = new LinkedList<>(); Class klass = entityType; @@ -66,7 +66,7 @@ public List> findReaders(ConfigurationImpl configuration, C while (!toProcess.isEmpty()) { Class iface = toProcess.poll(); List goodTypeReaders = readers.get(iface); - readerLookup(mediaType, runtimeType, mt, ret, goodTypeReaders); + readerLookup(mediaType, runtimeType, desired, ret, goodTypeReaders); for (Class i : iface.getInterfaces()) { if (!seen.contains(i)) { seen.add(i); @@ -76,7 +76,7 @@ public List> findReaders(ConfigurationImpl configuration, C } } List goodTypeReaders = readers.get(klass); - readerLookup(mediaType, runtimeType, mt, ret, goodTypeReaders); + readerLookup(mediaType, runtimeType, desired, ret, goodTypeReaders); if (klass.isInterface()) { klass = Object.class; } else { @@ -87,7 +87,8 @@ public List> findReaders(ConfigurationImpl configuration, C return ret; } - private void readerLookup(MediaType mediaType, RuntimeType runtimeType, List mt, List> ret, + private void readerLookup(MediaType mediaType, RuntimeType runtimeType, List desired, + List> ret, List goodTypeReaders) { if (goodTypeReaders != null && !goodTypeReaders.isEmpty()) { List mediaTypeMatchingReaders = new ArrayList<>(goodTypeReaders.size()); @@ -96,13 +97,15 @@ private void readerLookup(MediaType mediaType, RuntimeType runtimeType, List> findBuildTimeWriters(Class entityType, Runt protected List findResourceWriters(QuarkusMultivaluedMap, ResourceWriter> writers, Class klass, List produces, RuntimeType runtimeType) { + Class currentClass = klass; + List desired = MediaTypeHelper.getUngroupedMediaTypes(produces); List ret = new ArrayList<>(); Deque> toProcess = new LinkedList<>(); do { - if (klass == Object.class) { + if (currentClass == Object.class) { //spec extension, look for interfaces as well //we match interfaces before Object Set> seen = new HashSet<>(toProcess); while (!toProcess.isEmpty()) { Class iface = toProcess.poll(); List goodTypeWriters = writers.get(iface); - writerLookup(runtimeType, produces, ret, goodTypeWriters); + writerLookup(runtimeType, produces, desired, ret, goodTypeWriters); for (Class i : iface.getInterfaces()) { if (!seen.contains(i)) { seen.add(i); @@ -170,15 +175,16 @@ protected List findResourceWriters(QuarkusMultivaluedMap goodTypeWriters = writers.get(klass); - writerLookup(runtimeType, produces, ret, goodTypeWriters); - toProcess.addAll(Arrays.asList(klass.getInterfaces())); + List goodTypeWriters = writers.get(currentClass); + writerLookup(runtimeType, produces, desired, ret, goodTypeWriters); + toProcess.addAll(Arrays.asList(currentClass.getInterfaces())); // if we're an interface, pretend our superclass is Object to get us through the same logic as a class - if (klass.isInterface()) - klass = Object.class; - else - klass = klass.getSuperclass(); - } while (klass != null); + if (currentClass.isInterface()) { + currentClass = Object.class; + } else { + currentClass = currentClass.getSuperclass(); + } + } while (currentClass != null); return ret; } @@ -200,22 +206,25 @@ protected List> toMessageBodyWriters(List r return ret; } - private void writerLookup(RuntimeType runtimeType, List mt, List ret, - List goodTypeWriters) { + private void writerLookup(RuntimeType runtimeType, List produces, List desired, + List ret, List goodTypeWriters) { if (goodTypeWriters != null && !goodTypeWriters.isEmpty()) { List mediaTypeMatchingWriters = new ArrayList<>(goodTypeWriters.size()); + for (int i = 0; i < goodTypeWriters.size(); i++) { ResourceWriter goodTypeWriter = goodTypeWriters.get(i); if (!goodTypeWriter.matchesRuntimeType(runtimeType)) { continue; } - MediaType match = MediaTypeHelper.getFirstMatch(mt, goodTypeWriter.mediaTypes()); + MediaType match = MediaTypeHelper.getFirstMatch(desired, goodTypeWriter.mediaTypes()); if (match != null) { mediaTypeMatchingWriters.add(goodTypeWriter); } } + // we sort here because the spec mentions that the writers closer to the requested java type are tried first - mediaTypeMatchingWriters.sort(ResourceWriter.ResourceWriterComparator.INSTANCE); + mediaTypeMatchingWriters.sort(new ResourceWriter.ResourceWriterComparator(produces)); + ret.addAll(mediaTypeMatchingWriters); } } diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceReader.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceReader.java index 69ad09b953e4a..ca73e22378d91 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceReader.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceReader.java @@ -114,7 +114,11 @@ public boolean matchesRuntimeType(RuntimeType runtimeType) { */ public static class ResourceReaderComparator implements Comparator { - public static final ResourceReaderComparator INSTANCE = new ResourceReaderComparator(); + private final List produces; + + public ResourceReaderComparator(List produces) { + this.produces = produces; + } @Override public int compare(ResourceReader o1, ResourceReader o2) { @@ -144,6 +148,14 @@ public int compare(ResourceReader o1, ResourceReader o2) { return mediaTypeCompare; } + // try to compare using the number of matching produces media types + if (!produces.isEmpty()) { + mediaTypeCompare = MediaTypeHelper.compareMatchingMediaTypes(produces, mediaTypes1, mediaTypes2); + if (mediaTypeCompare != 0) { + return mediaTypeCompare; + } + } + // done to make the sorting result deterministic return Integer.compare(mediaTypes1.size(), mediaTypes2.size()); } diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceWriter.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceWriter.java index d04c4420c801d..381c363b60667 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceWriter.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/model/ResourceWriter.java @@ -99,7 +99,7 @@ public ServerMediaType serverMediaType() { if (serverMediaType == null) { synchronized (this) { // a MessageBodyWriter should always return its configured media type when negotiating, hence the 'false' for 'useSuffix' - serverMediaType = new ServerMediaType(mediaTypes(), StandardCharsets.UTF_8.name(), false, false); + serverMediaType = new ServerMediaType(mediaTypes(), StandardCharsets.UTF_8.name(), false); } } return serverMediaType; @@ -129,7 +129,15 @@ public String toString() { */ public static class ResourceWriterComparator implements Comparator { - public static final ResourceWriterComparator INSTANCE = new ResourceWriterComparator(); + private final List produces; + + public ResourceWriterComparator() { + this(Collections.emptyList()); + } + + public ResourceWriterComparator(List produces) { + this.produces = produces; + } @Override public int compare(ResourceWriter o1, ResourceWriter o2) { @@ -159,6 +167,14 @@ public int compare(ResourceWriter o1, ResourceWriter o2) { return mediaTypeCompare; } + // try to compare using the number of matching produces media types + if (!produces.isEmpty()) { + mediaTypeCompare = MediaTypeHelper.compareMatchingMediaTypes(produces, mediaTypes1, mediaTypes2); + if (mediaTypeCompare != 0) { + return mediaTypeCompare; + } + } + // done to make the sorting result deterministic return Integer.compare(mediaTypes1.size(), mediaTypes2.size()); } diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/util/MediaTypeHelper.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/util/MediaTypeHelper.java index 41b72877f9808..f7e5e84006736 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/util/MediaTypeHelper.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/util/MediaTypeHelper.java @@ -2,9 +2,11 @@ import java.io.Serializable; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.regex.Pattern; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @@ -16,6 +18,7 @@ public class MediaTypeHelper { public static final MediaTypeComparator Q_COMPARATOR = new MediaTypeComparator("q"); public static final MediaTypeComparator QS_COMPARATOR = new MediaTypeComparator("qs"); + private static final String MEDIA_TYPE_SUFFIX_DELIM = "+"; private static float getQTypeWithParamInfo(MediaType type, String parameterName) { if (type.getParameters() != null) { @@ -138,6 +141,13 @@ public static int compareWeight(MediaType one, MediaType two) { return Q_COMPARATOR.compare(one, two); } + public static int compareMatchingMediaTypes(List produces, List mediaTypes1, + List mediaTypes2) { + int countMediaTypes1 = countMatchingMediaTypes(produces, mediaTypes1); + int countMediaTypes2 = countMatchingMediaTypes(produces, mediaTypes2); + return (countMediaTypes1 < countMediaTypes2) ? 1 : ((countMediaTypes1 == countMediaTypes2) ? 0 : -1); + } + public static void sortByWeight(List types) { if (hasAtMostOneItem(types)) { return; @@ -267,20 +277,77 @@ public static boolean isUnsupportedWildcardSubtype(MediaType mediaType) { return false; } + public static List toListOfMediaType(String[] mediaTypes) { + if (mediaTypes == null || mediaTypes.length == 0) { + return Collections.emptyList(); + } + + List list = new ArrayList<>(mediaTypes.length); + for (String mediaType : mediaTypes) { + list.add(MediaType.valueOf(mediaType)); + } + + return Collections.unmodifiableList(list); + } + + /** + * This method ungroups the media types with suffix in separated media types. For example, having the media type + * "application/one+two" will return a list containing ["application/one+two", "application/one", "application/two"]. + * The Media Types without suffix remain as one media type. + * + * @param mediaTypes the list of media types to separate. + * @return the list of ungrouped media types. + */ + public static List getUngroupedMediaTypes(List mediaTypes) { + List effectiveMediaTypes = new ArrayList<>(); + for (MediaType mediaType : mediaTypes) { + effectiveMediaTypes.addAll(getUngroupedMediaTypes(mediaType)); + } + + return Collections.unmodifiableList(effectiveMediaTypes); + } + /** - * If the supplied media type contains a suffix in the subtype, then this returns a new media type - * that uses the suffix as the subtype + * This method ungroups the media type with suffix in separated media types. For example, having the media type + * "application/one+two" will return a list containing ["application/one+two", "application/one", "application/two"]. + * If the Media Type does not have a suffix, then it's not modified. + * + * @param mediaType the media type to separate. + * @return the list of ungrouped media types. */ - public static MediaType withSuffixAsSubtype(MediaType mediaType) { + public static List getUngroupedMediaTypes(MediaType mediaType) { if (mediaType == null) { - return null; + return Collections.emptyList(); + } + + if (mediaType.getSubtype() == null || !mediaType.getSubtype().contains(MEDIA_TYPE_SUFFIX_DELIM)) { + return Collections.singletonList(mediaType); + } + + String[] subTypes = mediaType.getSubtype().split(Pattern.quote(MEDIA_TYPE_SUFFIX_DELIM)); + + List effectiveMediaTypes = new ArrayList<>(1 + subTypes.length); + effectiveMediaTypes.add(mediaType); + for (String subType : subTypes) { + effectiveMediaTypes.add(new MediaType(mediaType.getType(), subType, mediaType.getParameters())); } - int plusIndex = mediaType.getSubtype().indexOf('+'); - if ((plusIndex > -1) && (plusIndex < mediaType.getSubtype().length() - 1)) { - mediaType = new MediaType(mediaType.getType(), - mediaType.getSubtype().substring(plusIndex + 1), - mediaType.getParameters()); + + return Collections.unmodifiableList(effectiveMediaTypes); + } + + private static int countMatchingMediaTypes(List produces, List mediaTypes) { + int count = 0; + for (int i = 0; i < mediaTypes.size(); i++) { + MediaType mediaType = mediaTypes.get(i); + for (int j = 0; j < produces.size(); j++) { + MediaType produce = produces.get(j); + if (mediaType.isCompatible(produce)) { + count++; + break; + } + } } - return mediaType; + + return count; } } diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/util/ServerMediaType.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/util/ServerMediaType.java index cff31dd2a3200..97ba2d2f174be 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/util/ServerMediaType.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/util/ServerMediaType.java @@ -30,14 +30,11 @@ public static List mediaTypesFromArray(String[] mediaTypesStrs) { } /** - * * @param mediaTypes The original media types * @param charset charset to use * @param deprioritizeWildcards whether or not wildcard types should be carry less weight when sorting is performed - * @param useSuffix whether or not a media type whose subtype contains a suffix should swap the entire subtype with the - * suffix */ - public ServerMediaType(List mediaTypes, String charset, boolean deprioritizeWildcards, boolean useSuffix) { + public ServerMediaType(List mediaTypes, String charset, boolean deprioritizeWildcards) { if (mediaTypes.isEmpty()) { this.sortedOriginalMediaTypes = new MediaType[] { MediaType.WILDCARD_TYPE }; } else { @@ -87,12 +84,6 @@ public int compare(MediaType m1, MediaType m2) { MediaType m = new MediaType(existing.getType(), existing.getSubtype(), charset); sortedMediaTypes[i] = m; } - // use the suffix type if it exists when negotiating the type - if (useSuffix) { - for (int i = 0; i < sortedMediaTypes.length; i++) { - sortedMediaTypes[i] = MediaTypeHelper.withSuffixAsSubtype(sortedMediaTypes[i]); - } - } // if there is only one media type, use it if (sortedMediaTypes.length == 1 && !(sortedMediaTypes[0].isWildcardType() || sortedMediaTypes[0].isWildcardSubtype())) { diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/serialization/DynamicEntityWriter.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/serialization/DynamicEntityWriter.java index 4109b8e1182a7..929017cdc6bd7 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/serialization/DynamicEntityWriter.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/serialization/DynamicEntityWriter.java @@ -97,8 +97,7 @@ public void write(ResteasyReactiveRequestContext context, Object entity) throws } } else { writers = serialisers - .findWriters(null, entity.getClass(), MediaTypeHelper.withSuffixAsSubtype(producesMediaType.getMediaType()), - RuntimeType.SERVER) + .findWriters(null, entity.getClass(), producesMediaType.getMediaType(), RuntimeType.SERVER) .toArray(ServerSerialisers.NO_WRITER); } for (MessageBodyWriter w : writers) { diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeResourceDeployment.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeResourceDeployment.java index e155cc0c16a5a..c8e634f48f7b1 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeResourceDeployment.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeResourceDeployment.java @@ -363,7 +363,7 @@ public RuntimeResource buildResourceMethod(ResourceClass clazz, // when negotiating a media type, we want to use the proper subtype to locate a ResourceWriter, // hence the 'true' for 'useSuffix' serverMediaType = new ServerMediaType(ServerMediaType.mediaTypesFromArray(method.getProduces()), - StandardCharsets.UTF_8.name(), false, true); + StandardCharsets.UTF_8.name(), false); } if (method.getHttpMethod() == null) { //this is a resource locator method @@ -383,8 +383,7 @@ public RuntimeResource buildResourceMethod(ResourceClass clazz, } else if (rawEffectiveReturnType != Void.class && rawEffectiveReturnType != void.class) { List> buildTimeWriters = serialisers.findBuildTimeWriters(rawEffectiveReturnType, - RuntimeType.SERVER, Collections.singletonList( - MediaTypeHelper.withSuffixAsSubtype(MediaType.valueOf(method.getProduces()[0])))); + RuntimeType.SERVER, MediaTypeHelper.toListOfMediaType(method.getProduces())); if (buildTimeWriters == null) { //if this is null this means that the type cannot be resolved at build time //this happens when the method returns a generic type (e.g. Object), so there diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/MediaTypeMapper.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/MediaTypeMapper.java index 1d2bea0c74983..a77db34a0d847 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/MediaTypeMapper.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/MediaTypeMapper.java @@ -131,8 +131,9 @@ public void setResource(RuntimeResource runtimeResource, MediaType mediaType) { } public void setupServerMediaType() { - MediaTypeHelper.sortByQSWeight(mtsWithParams); // TODO: this isn't completely correct as we are supposed to take q and then qs into account... - serverMediaType = new ServerMediaType(mtsWithParams, StandardCharsets.UTF_8.name(), true, false); + // TODO: this isn't completely correct as we are supposed to take q and then qs into account... + MediaTypeHelper.sortByQSWeight(mtsWithParams); + serverMediaType = new ServerMediaType(mtsWithParams, StandardCharsets.UTF_8.name(), true); } } } diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/RequestDeserializeHandler.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/RequestDeserializeHandler.java index 2a89c7b1f4c1d..d668916b5851d 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/RequestDeserializeHandler.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/RequestDeserializeHandler.java @@ -15,7 +15,6 @@ import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.ReaderInterceptor; import org.jboss.logging.Logger; -import org.jboss.resteasy.reactive.common.util.MediaTypeHelper; import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; import org.jboss.resteasy.reactive.server.core.ServerSerialisers; import org.jboss.resteasy.reactive.server.jaxrs.ReaderInterceptorContextImpl; @@ -47,7 +46,7 @@ public void handle(ResteasyReactiveRequestContext requestContext) throws Excepti String requestTypeString = requestContext.serverRequest().getRequestHeader(HttpHeaders.CONTENT_TYPE); if (requestTypeString != null) { try { - effectiveRequestType = MediaTypeHelper.withSuffixAsSubtype(MediaType.valueOf(requestTypeString)); + effectiveRequestType = MediaType.valueOf(requestTypeString); } catch (Exception e) { throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST).build()); } From 4783eab84304e652016b3f61c9928fd765ef679c Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Tue, 24 May 2022 10:06:03 +0200 Subject: [PATCH 14/14] Clarify the situation regarding short names --- docs/src/main/asciidoc/podman.adoc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/src/main/asciidoc/podman.adoc b/docs/src/main/asciidoc/podman.adoc index 06fbcd0f95010..ca8268f6ed5bb 100644 --- a/docs/src/main/asciidoc/podman.adoc +++ b/docs/src/main/asciidoc/podman.adoc @@ -50,8 +50,10 @@ echo "export TESTCONTAINERS_RYUK_DISABLED=true" >> ~/.bashrc Testcontainers and Quarkus Dev Services also expect the container service they make requests against to be non-interactive. In case you have multiple registries configured in your Docker or Podman configuration, and when using short image names, Podman responds with a prompt asking which registry should be used to pull images. -We recommend you to avoid short names and always use fully specified names including the registry. -If, for some reason, it is not an option for you, you can disable this prompt by setting the `short-name-mode="disabled"` configuration property of Podman in `/etc/containers/registries.conf`. +While we recommend you to avoid short names and always use fully specified names including the registry, +Testcontainers unfortunately relies on short names internally for the time being. +If you are using Testcontainers, either directly or through Dev Services, +you need to disable this prompt by setting the `short-name-mode="disabled"` configuration property of Podman in `/etc/containers/registries.conf`. == Other operating systems @@ -72,4 +74,4 @@ alias docker='podman' For more details, please see the https://podman.io/getting-started/installation#macos[official Podman documentation] and this https://www.redhat.com/sysadmin/replace-docker-podman-macos[article]. .Windows -Please see the https://github.com/containers/podman/blob/main/docs/tutorials/podman-for-windows.md[Podman for Windows guide] for setup and usage instructions. \ No newline at end of file +Please see the https://github.com/containers/podman/blob/main/docs/tutorials/podman-for-windows.md[Podman for Windows guide] for setup and usage instructions.