From d17de3da5767eb9fd4d39780d99fdc46710c94b0 Mon Sep 17 00:00:00 2001 From: Zheng Feng Date: Sun, 26 Jun 2022 23:28:23 +0800 Subject: [PATCH 1/5] Fix #54 to add DevServices for Artemis JMS --- .../deployment/ArtemisBuildTimeConfig.java | 6 + .../ArtemisDevServicesBuildTimeConfig.java | 71 ++++++ .../pages/includes/quarkus-artemis-core.adoc | 67 +++++ .../src/main/resources/application.properties | 4 +- .../io/quarkus/it/artemis/ArtemisHelper.java | 4 +- .../it/artemis/ArtemisProducerTest.java | 21 +- .../it/artemis/ArtemisProducerXATest.java | 6 +- .../it/artemis/BaseArtemisProducerTest.java | 25 ++ .../it/artemis/DevServiceArtemisEnabled.java | 21 ++ .../DevServiceArtemisProducerTest.java | 15 ++ jms/deployment/pom.xml | 15 +- .../DevServicesArtemisJmsProcessor.java | 231 ++++++++++++++++++ .../artemis/test/ArtemisTestResource.java | 9 + 13 files changed, 471 insertions(+), 24 deletions(-) create mode 100644 core/deployment/src/main/java/io/quarkus/artemis/core/deployment/ArtemisDevServicesBuildTimeConfig.java create mode 100644 integration-tests/jms/src/test/java/io/quarkus/it/artemis/BaseArtemisProducerTest.java create mode 100644 integration-tests/jms/src/test/java/io/quarkus/it/artemis/DevServiceArtemisEnabled.java create mode 100644 integration-tests/jms/src/test/java/io/quarkus/it/artemis/DevServiceArtemisProducerTest.java create mode 100644 jms/deployment/src/main/java/io/quarkus/artemis/jms/deployment/DevServicesArtemisJmsProcessor.java diff --git a/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/ArtemisBuildTimeConfig.java b/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/ArtemisBuildTimeConfig.java index 31258f3a..19617765 100644 --- a/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/ArtemisBuildTimeConfig.java +++ b/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/ArtemisBuildTimeConfig.java @@ -18,4 +18,10 @@ public class ArtemisBuildTimeConfig { */ @ConfigItem(name = "xa.enabled", defaultValue = "false") public boolean xaEnabled; + + /** + * Configuration for DevServices. DevServices allows Quarkus to automatically start ActiveMQ Artemis in dev and test mode. + */ + @ConfigItem + public ArtemisDevServicesBuildTimeConfig devservices; } diff --git a/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/ArtemisDevServicesBuildTimeConfig.java b/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/ArtemisDevServicesBuildTimeConfig.java new file mode 100644 index 00000000..b047a654 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/ArtemisDevServicesBuildTimeConfig.java @@ -0,0 +1,71 @@ +package io.quarkus.artemis.core.deployment; + +import java.util.Optional; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; + +@ConfigGroup +public class ArtemisDevServicesBuildTimeConfig { + + /** + * If Dev Services for ActiveMQ Artemis has been explicitly enabled or disabled. Dev Services are generally enabled + * by default, unless there is an existing configuration present. For Artemis, Dev Services starts a broker unless + * {@code quarkus.artemis.url} is set. + */ + @ConfigItem + public Optional enabled = Optional.empty(); + + /** + * Optional fixed port the dev service will listen to. + *

+ * If not defined, the port will be chosen randomly. + */ + @ConfigItem + public Optional port; + + /** + * The ActiveMQ Artemis container image to use. + */ + @ConfigItem(defaultValue = "quay.io/artemiscloud/activemq-artemis-broker:1.0.5") + public String imageName; + + /** + * Indicates if the ActiveMQ Artemis broker managed by Quarkus Dev Services is shared. + * When shared, Quarkus looks for running containers using label-based service discovery. + * If a matching container is found, it is used, and so a second one is not started. + * Otherwise, Dev Services for ActiveMQ Artemis starts a new container. + *

+ * The discovery uses the {@code quarkus-dev-service-artemis} label. + * The value is configured using the {@code service-name} property. + *

+ * Container sharing is only used in dev mode. + */ + @ConfigItem(defaultValue = "true") + public boolean shared; + + /** + * The value of the {@code quarkus-dev-service-artemis} label attached to the started container. + * This property is used when {@code shared} is set to {@code true}. + * In this case, before starting a container, Dev Services for ActiveMQ Artemis looks for a container with the + * {@code quarkus-dev-service-artemis} label + * set to the configured value. If found, it will use this container instead of starting a new one. Otherwise it + * starts a new container with the {@code quarkus-dev-service-artemis} label set to the specified value. + *

+ * This property is used when you need multiple shared ActiveMQ Artemis brokers. + */ + @ConfigItem(defaultValue = "artemis") + public String serviceName; + + /** + * User to start artemis broker + */ + @ConfigItem(defaultValue = "admin") + public String user; + + /** + * Password to start artemis broker + */ + @ConfigItem(defaultValue = "admin") + public String password; +} diff --git a/docs/modules/ROOT/pages/includes/quarkus-artemis-core.adoc b/docs/modules/ROOT/pages/includes/quarkus-artemis-core.adoc index d2c8e9a9..a30830a6 100644 --- a/docs/modules/ROOT/pages/includes/quarkus-artemis-core.adoc +++ b/docs/modules/ROOT/pages/includes/quarkus-artemis-core.adoc @@ -26,6 +26,73 @@ Support to expose `javax.jms.XAConnectionFactory` |`false` +a|icon:lock[title=Fixed at build time] [[quarkus-artemis-core_quarkus.artemis.devservices.enabled]]`link:#quarkus-artemis-core_quarkus.artemis.devservices.enabled[quarkus.artemis.devservices.enabled]` + +[.description] +-- +If Dev Services for ActiveMQ Artemis has been explicitly enabled or disabled. Dev Services are generally enabled by default, unless there is an existing configuration present. For Artemis, Dev Services starts a broker unless `quarkus.artemis.url` is set. +--|boolean +| + + +a|icon:lock[title=Fixed at build time] [[quarkus-artemis-core_quarkus.artemis.devservices.port]]`link:#quarkus-artemis-core_quarkus.artemis.devservices.port[quarkus.artemis.devservices.port]` + +[.description] +-- +Optional fixed port the dev service will listen to. + If not defined, the port will be chosen randomly. +--|int +| + + +a|icon:lock[title=Fixed at build time] [[quarkus-artemis-core_quarkus.artemis.devservices.image-name]]`link:#quarkus-artemis-core_quarkus.artemis.devservices.image-name[quarkus.artemis.devservices.image-name]` + +[.description] +-- +The ActiveMQ Artemis container image to use. +--|string +|`quay.io/artemiscloud/activemq-artemis-broker:1.0.5` + + +a|icon:lock[title=Fixed at build time] [[quarkus-artemis-core_quarkus.artemis.devservices.shared]]`link:#quarkus-artemis-core_quarkus.artemis.devservices.shared[quarkus.artemis.devservices.shared]` + +[.description] +-- +Indicates if the ActiveMQ Artemis broker managed by Quarkus Dev Services is shared. When shared, Quarkus looks for running containers using label-based service discovery. If a matching container is found, it is used, and so a second one is not started. Otherwise, Dev Services for ActiveMQ Artemis starts a new container. + The discovery uses the `quarkus-dev-service-artemis` label. The value is configured using the `service-name` property. + Container sharing is only used in dev mode. +--|boolean +|`true` + + +a|icon:lock[title=Fixed at build time] [[quarkus-artemis-core_quarkus.artemis.devservices.service-name]]`link:#quarkus-artemis-core_quarkus.artemis.devservices.service-name[quarkus.artemis.devservices.service-name]` + +[.description] +-- +The value of the `quarkus-dev-service-artemis` label attached to the started container. This property is used when `shared` is set to `true`. In this case, before starting a container, Dev Services for ActiveMQ Artemis looks for a container with the `quarkus-dev-service-artemis` label set to the configured value. If found, it will use this container instead of starting a new one. Otherwise it starts a new container with the `quarkus-dev-service-artemis` label set to the specified value. + This property is used when you need multiple shared ActiveMQ Artemis brokers. +--|string +|`artemis` + + +a|icon:lock[title=Fixed at build time] [[quarkus-artemis-core_quarkus.artemis.devservices.user]]`link:#quarkus-artemis-core_quarkus.artemis.devservices.user[quarkus.artemis.devservices.user]` + +[.description] +-- +User to start artemis broker +--|string +|`admin` + + +a|icon:lock[title=Fixed at build time] [[quarkus-artemis-core_quarkus.artemis.devservices.password]]`link:#quarkus-artemis-core_quarkus.artemis.devservices.password[quarkus.artemis.devservices.password]` + +[.description] +-- +Password to start artemis broker +--|string +|`admin` + + a| [[quarkus-artemis-core_quarkus.artemis.url]]`link:#quarkus-artemis-core_quarkus.artemis.url[quarkus.artemis.url]` [.description] diff --git a/integration-tests/jms/src/main/resources/application.properties b/integration-tests/jms/src/main/resources/application.properties index 8f872bdc..65e531c5 100644 --- a/integration-tests/jms/src/main/resources/application.properties +++ b/integration-tests/jms/src/main/resources/application.properties @@ -1,2 +1,2 @@ -quarkus.artemis.xa.enabled=true -quarkus.artemis.url=tcp://localhost:61616 +quarkus.artemis.devservices.enabled=false +quarkus.artemis.xa.enabled=true \ No newline at end of file diff --git a/integration-tests/jms/src/test/java/io/quarkus/it/artemis/ArtemisHelper.java b/integration-tests/jms/src/test/java/io/quarkus/it/artemis/ArtemisHelper.java index 2a696c85..96b12d5d 100644 --- a/integration-tests/jms/src/test/java/io/quarkus/it/artemis/ArtemisHelper.java +++ b/integration-tests/jms/src/test/java/io/quarkus/it/artemis/ArtemisHelper.java @@ -6,6 +6,7 @@ import javax.jms.JMSException; import org.apache.activemq.artemis.jms.client.ActiveMQJMSConnectionFactory; +import org.eclipse.microprofile.config.ConfigProvider; public interface ArtemisHelper { @@ -14,6 +15,7 @@ default String createBody() { } default JMSContext createContext() throws JMSException { - return new ActiveMQJMSConnectionFactory("tcp://localhost:61616").createContext(JMSContext.AUTO_ACKNOWLEDGE); + String url = ConfigProvider.getConfig().getValue("quarkus.artemis.url", String.class); + return new ActiveMQJMSConnectionFactory(url).createContext(JMSContext.AUTO_ACKNOWLEDGE); } } diff --git a/integration-tests/jms/src/test/java/io/quarkus/it/artemis/ArtemisProducerTest.java b/integration-tests/jms/src/test/java/io/quarkus/it/artemis/ArtemisProducerTest.java index e4705cdf..d42bcebc 100644 --- a/integration-tests/jms/src/test/java/io/quarkus/it/artemis/ArtemisProducerTest.java +++ b/integration-tests/jms/src/test/java/io/quarkus/it/artemis/ArtemisProducerTest.java @@ -1,33 +1,16 @@ package io.quarkus.it.artemis; -import javax.jms.JMSConsumer; -import javax.jms.JMSContext; -import javax.jms.Message; -import javax.ws.rs.core.Response.Status; - -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import io.quarkus.artemis.test.ArtemisTestResource; import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.junit.QuarkusTest; -import io.restassured.RestAssured; -import io.restassured.response.Response; @QuarkusTest @QuarkusTestResource(ArtemisTestResource.class) -public class ArtemisProducerTest implements ArtemisHelper { - +public class ArtemisProducerTest extends BaseArtemisProducerTest { @Test public void test() throws Exception { - String body = createBody(); - Response response = RestAssured.with().body(body).post("/artemis"); - Assertions.assertEquals(Status.NO_CONTENT.getStatusCode(), response.statusCode()); - - try (JMSContext context = createContext()) { - JMSConsumer consumer = context.createConsumer(context.createQueue("test-jms")); - Message message = consumer.receive(1000L); - Assertions.assertEquals(body, message.getBody(String.class)); - } + super.test(); } } diff --git a/integration-tests/jms/src/test/java/io/quarkus/it/artemis/ArtemisProducerXATest.java b/integration-tests/jms/src/test/java/io/quarkus/it/artemis/ArtemisProducerXATest.java index c6ac3dfc..404f0c7f 100644 --- a/integration-tests/jms/src/test/java/io/quarkus/it/artemis/ArtemisProducerXATest.java +++ b/integration-tests/jms/src/test/java/io/quarkus/it/artemis/ArtemisProducerXATest.java @@ -15,7 +15,11 @@ @QuarkusTest @QuarkusTestResource(ArtemisTestResource.class) -public class ArtemisProducerXATest implements ArtemisHelper { +public class ArtemisProducerXATest extends BaseArtemisProducerTest { + @Test + public void test() throws Exception { + super.test(); + } @Test public void testXA() throws Exception { diff --git a/integration-tests/jms/src/test/java/io/quarkus/it/artemis/BaseArtemisProducerTest.java b/integration-tests/jms/src/test/java/io/quarkus/it/artemis/BaseArtemisProducerTest.java new file mode 100644 index 00000000..8294f3d2 --- /dev/null +++ b/integration-tests/jms/src/test/java/io/quarkus/it/artemis/BaseArtemisProducerTest.java @@ -0,0 +1,25 @@ +package io.quarkus.it.artemis; + +import javax.jms.JMSConsumer; +import javax.jms.JMSContext; +import javax.jms.Message; + +import org.junit.jupiter.api.Assertions; + +import io.restassured.RestAssured; +import io.restassured.response.Response; + +abstract public class BaseArtemisProducerTest implements ArtemisHelper { + + public void test() throws Exception { + String body = createBody(); + Response response = RestAssured.with().body(body).post("/artemis"); + Assertions.assertEquals(javax.ws.rs.core.Response.Status.NO_CONTENT.getStatusCode(), response.statusCode()); + + try (JMSContext context = createContext()) { + JMSConsumer consumer = context.createConsumer(context.createQueue("test-jms")); + Message message = consumer.receive(1000L); + Assertions.assertEquals(body, message.getBody(String.class)); + } + } +} diff --git a/integration-tests/jms/src/test/java/io/quarkus/it/artemis/DevServiceArtemisEnabled.java b/integration-tests/jms/src/test/java/io/quarkus/it/artemis/DevServiceArtemisEnabled.java new file mode 100644 index 00000000..378e5a54 --- /dev/null +++ b/integration-tests/jms/src/test/java/io/quarkus/it/artemis/DevServiceArtemisEnabled.java @@ -0,0 +1,21 @@ +package io.quarkus.it.artemis; + +import java.util.Collections; +import java.util.Map; + +import io.quarkus.test.junit.QuarkusTestProfile; + +public class DevServiceArtemisEnabled implements QuarkusTestProfile { + public DevServiceArtemisEnabled() { + } + + @Override + public Map getConfigOverrides() { + return Collections.singletonMap("quarkus.artemis.devservices.enabled", "true"); + } + + @Override + public boolean disableGlobalTestResources() { + return true; + } +} diff --git a/integration-tests/jms/src/test/java/io/quarkus/it/artemis/DevServiceArtemisProducerTest.java b/integration-tests/jms/src/test/java/io/quarkus/it/artemis/DevServiceArtemisProducerTest.java new file mode 100644 index 00000000..5b7cd1e5 --- /dev/null +++ b/integration-tests/jms/src/test/java/io/quarkus/it/artemis/DevServiceArtemisProducerTest.java @@ -0,0 +1,15 @@ +package io.quarkus.it.artemis; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.TestProfile; + +@QuarkusTest +@TestProfile(DevServiceArtemisEnabled.class) +public class DevServiceArtemisProducerTest extends BaseArtemisProducerTest { + @Test + public void test() throws Exception { + super.test(); + } +} diff --git a/jms/deployment/pom.xml b/jms/deployment/pom.xml index 00c19516..b6b23c09 100644 --- a/jms/deployment/pom.xml +++ b/jms/deployment/pom.xml @@ -15,7 +15,20 @@ io.quarkiverse.artemis quarkus-artemis-core-deployment - + + io.quarkus + quarkus-devservices-deployment + + + org.testcontainers + testcontainers + + + junit + junit + + + io.quarkiverse.artemis quarkus-artemis-jms diff --git a/jms/deployment/src/main/java/io/quarkus/artemis/jms/deployment/DevServicesArtemisJmsProcessor.java b/jms/deployment/src/main/java/io/quarkus/artemis/jms/deployment/DevServicesArtemisJmsProcessor.java new file mode 100644 index 00000000..d2e9d684 --- /dev/null +++ b/jms/deployment/src/main/java/io/quarkus/artemis/jms/deployment/DevServicesArtemisJmsProcessor.java @@ -0,0 +1,231 @@ +package io.quarkus.artemis.jms.deployment; + +import java.time.Duration; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; + +import org.jboss.logging.Logger; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; + +import io.quarkus.artemis.core.deployment.ArtemisBuildTimeConfig; +import io.quarkus.artemis.core.deployment.ArtemisDevServicesBuildTimeConfig; +import io.quarkus.deployment.IsNormal; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.CuratedApplicationShutdownBuildItem; +import io.quarkus.deployment.builditem.DevServicesResultBuildItem; +import io.quarkus.deployment.builditem.DevServicesResultBuildItem.RunningDevService; +import io.quarkus.deployment.builditem.DevServicesSharedNetworkBuildItem; +import io.quarkus.deployment.builditem.DockerStatusBuildItem; +import io.quarkus.deployment.builditem.LaunchModeBuildItem; +import io.quarkus.deployment.console.ConsoleInstalledBuildItem; +import io.quarkus.deployment.console.StartupLogCompressor; +import io.quarkus.deployment.dev.devservices.GlobalDevServicesConfig; +import io.quarkus.deployment.logging.LoggingSetupBuildItem; +import io.quarkus.devservices.common.ConfigureUtil; +import io.quarkus.devservices.common.ContainerAddress; +import io.quarkus.devservices.common.ContainerLocator; +import io.quarkus.runtime.configuration.ConfigUtils; + +/** + * Start a ActiveMQ Artemis broker if needed + */ +public class DevServicesArtemisJmsProcessor { + private static final Logger LOGGER = Logger.getLogger(DevServicesArtemisJmsProcessor.class); + private static final String QUARKUS_ARTEMIS_URL = "quarkus.artemis.url"; + + /** + * Label to add to shared Dev Service for ActiveMQ Artemis running in containers. + * This allows other applications to discover the running service and use it instead of starting a new instance. + */ + static final String DEV_SERVICE_LABEL = "quarkus-dev-service-artemis"; + static final int ARTEMIS_PORT = 61616; + + private static final ContainerLocator artemisContainerLocator = new ContainerLocator(DEV_SERVICE_LABEL, ARTEMIS_PORT); + + static volatile RunningDevService devService; + static volatile ArtemisDevServiceCfg cfg; + static volatile boolean first = true; + + @BuildStep(onlyIfNot = IsNormal.class, onlyIf = GlobalDevServicesConfig.Enabled.class) + public DevServicesResultBuildItem startArtemisDevService( + DockerStatusBuildItem dockerStatusBuildItem, + LaunchModeBuildItem launchMode, + ArtemisBuildTimeConfig artemisBuildTimeConfig, + List devServicesSharedNetworkBuildItem, + Optional consoleInstalledBuildItem, + CuratedApplicationShutdownBuildItem closeBuildItem, + LoggingSetupBuildItem loggingSetupBuildItem, GlobalDevServicesConfig devServicesConfig) { + + ArtemisDevServiceCfg configuration = getConfiguration(artemisBuildTimeConfig); + + if (devService != null) { + boolean shouldShutdownTheBroker = !configuration.equals(cfg); + if (!shouldShutdownTheBroker) { + return devService.toBuildItem(); + } + shutdownBroker(); + cfg = null; + } + + StartupLogCompressor compressor = new StartupLogCompressor( + (launchMode.isTest() ? "(test) " : "") + "ActiveMQ Artemis Dev Services Starting:", + consoleInstalledBuildItem, loggingSetupBuildItem); + try { + devService = startArtemis(dockerStatusBuildItem, configuration, launchMode, + !devServicesSharedNetworkBuildItem.isEmpty(), + devServicesConfig.timeout); + if (devService == null) { + compressor.closeAndDumpCaptured(); + } else { + compressor.close(); + } + } catch (Throwable t) { + compressor.closeAndDumpCaptured(); + throw new RuntimeException(t); + } + + if (devService == null) { + return null; + } + + // Configure the watch dog + if (first) { + first = false; + Runnable closeTask = () -> { + if (devService != null) { + shutdownBroker(); + } + first = true; + devService = null; + cfg = null; + }; + closeBuildItem.addCloseTask(closeTask, true); + } + cfg = configuration; + + if (devService.isOwner()) { + LOGGER.infof("Dev Services for ActiveMQ Artemis started on %s", getArtemisUrl()); + } + + return devService.toBuildItem(); + } + + public static String getArtemisUrl() { + return devService.getConfig().get(QUARKUS_ARTEMIS_URL); + } + + private void shutdownBroker() { + if (devService != null) { + try { + devService.close(); + } catch (Throwable e) { + LOGGER.error("Failed to stop the ActiveMQ Artemis broker", e); + } finally { + devService = null; + } + } + } + + private RunningDevService startArtemis(DockerStatusBuildItem dockerStatusBuildItem, ArtemisDevServiceCfg config, + LaunchModeBuildItem launchMode, boolean useSharedNetwork, Optional timeout) { + if (!config.devServicesEnabled) { + // explicitly disabled + LOGGER.debug("Not starting dev services for ActiveMQ Artemis, as it has been disabled in the config."); + return null; + } + + // Check if quarkus.artemis.url is set + if (ConfigUtils.isPropertyPresent(QUARKUS_ARTEMIS_URL)) { + LOGGER.debug("Not starting dev services for ActiveMQ Artemis, the quarkus.artemis.url is configured."); + return null; + } + + if (!dockerStatusBuildItem.isDockerAvailable()) { + LOGGER.warn( + "Docker isn't working, please configure the ActiveMQ Artemis Url property (quarkus.artemis.url)."); + return null; + } + + final Optional maybeContainerAddress = artemisContainerLocator.locateContainer(config.serviceName, + config.shared, + launchMode.getLaunchMode()); + + // Starting the broker + final Supplier defaultArtemisBrokerSupplier = () -> { + GenericContainer container = new GenericContainer<>(config.imageName) + .withExposedPorts(ARTEMIS_PORT) + .withEnv("AMQ_USER", config.user) + .withEnv("AMQ_PASSWORD", config.password) + .waitingFor(Wait.forLogMessage(".* Apache ActiveMQ Artemis Message Broker .*", 1)); + + ConfigureUtil.configureSharedNetwork(container, "artemis"); + + if (config.serviceName != null) { + container.withLabel(DevServicesArtemisJmsProcessor.DEV_SERVICE_LABEL, config.serviceName); + } + + timeout.ifPresent(container::withStartupTimeout); + + container.start(); + return new RunningDevService("ActiveMQ-Artemis", + container.getContainerId(), + container::close, + QUARKUS_ARTEMIS_URL, String.format("tcp://localhost:%d", container.getMappedPort(ARTEMIS_PORT))); + }; + + return maybeContainerAddress + .map(containerAddress -> new RunningDevService("ActiveMQ-Artemis", + containerAddress.getId(), + null, + QUARKUS_ARTEMIS_URL, containerAddress.getUrl())) + .orElseGet(defaultArtemisBrokerSupplier); + } + + private ArtemisDevServiceCfg getConfiguration(ArtemisBuildTimeConfig cfg) { + ArtemisDevServicesBuildTimeConfig devServicesConfig = cfg.devservices; + return new ArtemisDevServiceCfg(devServicesConfig); + } + + private static final class ArtemisDevServiceCfg { + private final boolean devServicesEnabled; + private final String imageName; + private final Integer fixedExposedPort; + private final boolean shared; + private final String serviceName; + + private final String user; + + private final String password; + + public ArtemisDevServiceCfg(ArtemisDevServicesBuildTimeConfig config) { + this.devServicesEnabled = config.enabled.orElse(true); + this.imageName = config.imageName; + this.fixedExposedPort = config.port.orElse(0); + this.shared = config.shared; + this.serviceName = config.serviceName; + this.user = config.user; + this.password = config.password; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ArtemisDevServiceCfg that = (ArtemisDevServiceCfg) o; + return devServicesEnabled == that.devServicesEnabled && Objects.equals(imageName, that.imageName) + && Objects.equals(fixedExposedPort, that.fixedExposedPort); + } + + @Override + public int hashCode() { + return Objects.hash(devServicesEnabled, imageName, fixedExposedPort); + } + } +} diff --git a/test-framework/src/main/java/io/quarkus/artemis/test/ArtemisTestResource.java b/test-framework/src/main/java/io/quarkus/artemis/test/ArtemisTestResource.java index 0637ca36..ca50bf51 100644 --- a/test-framework/src/main/java/io/quarkus/artemis/test/ArtemisTestResource.java +++ b/test-framework/src/main/java/io/quarkus/artemis/test/ArtemisTestResource.java @@ -4,6 +4,7 @@ import java.util.Collections; import java.util.Map; +import org.apache.activemq.artemis.api.core.TransportConfiguration; import org.apache.activemq.artemis.core.server.embedded.EmbeddedActiveMQ; import org.apache.commons.io.FileUtils; @@ -22,6 +23,14 @@ public Map start() { } catch (Exception e) { throw new RuntimeException("Could not start embedded ActiveMQ server", e); } + + for (TransportConfiguration config : embedded.getConfiguration().getAcceptorConfigurations()) { + if (config.getName().equals("activemq")) { + return Collections.singletonMap("quarkus.artemis.url", + String.format("tcp://%s:%s", config.getParams().get("host"), config.getParams().get("port"))); + } + } + return Collections.emptyMap(); } From 1812d09272b3e1ecba86414b04ecf283218b8663 Mon Sep 17 00:00:00 2001 From: Zheng Feng Date: Tue, 28 Jun 2022 19:46:12 +0800 Subject: [PATCH 2/5] Move DevServicesArtemisProcessor to core --- core/deployment/pom.xml | 16 ++++++++++++++++ .../deployment/DevServicesArtemisProcessor.java | 10 ++++------ jms/deployment/pom.xml | 14 -------------- 3 files changed, 20 insertions(+), 20 deletions(-) rename jms/deployment/src/main/java/io/quarkus/artemis/jms/deployment/DevServicesArtemisJmsProcessor.java => core/deployment/src/main/java/io/quarkus/artemis/core/deployment/DevServicesArtemisProcessor.java (96%) diff --git a/core/deployment/pom.xml b/core/deployment/pom.xml index 40a2ffc4..fa3921cf 100644 --- a/core/deployment/pom.xml +++ b/core/deployment/pom.xml @@ -21,6 +21,11 @@ quarkus-arc-deployment + + io.quarkus + quarkus-devservices-deployment + + io.quarkus quarkus-jsonp-deployment @@ -40,6 +45,17 @@ io.quarkiverse.artemis quarkus-artemis-core + + + org.testcontainers + testcontainers + + + junit + junit + + + diff --git a/jms/deployment/src/main/java/io/quarkus/artemis/jms/deployment/DevServicesArtemisJmsProcessor.java b/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/DevServicesArtemisProcessor.java similarity index 96% rename from jms/deployment/src/main/java/io/quarkus/artemis/jms/deployment/DevServicesArtemisJmsProcessor.java rename to core/deployment/src/main/java/io/quarkus/artemis/core/deployment/DevServicesArtemisProcessor.java index d2e9d684..d07d48f5 100644 --- a/jms/deployment/src/main/java/io/quarkus/artemis/jms/deployment/DevServicesArtemisJmsProcessor.java +++ b/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/DevServicesArtemisProcessor.java @@ -1,4 +1,4 @@ -package io.quarkus.artemis.jms.deployment; +package io.quarkus.artemis.core.deployment; import java.time.Duration; import java.util.List; @@ -10,8 +10,6 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; -import io.quarkus.artemis.core.deployment.ArtemisBuildTimeConfig; -import io.quarkus.artemis.core.deployment.ArtemisDevServicesBuildTimeConfig; import io.quarkus.deployment.IsNormal; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.CuratedApplicationShutdownBuildItem; @@ -32,8 +30,8 @@ /** * Start a ActiveMQ Artemis broker if needed */ -public class DevServicesArtemisJmsProcessor { - private static final Logger LOGGER = Logger.getLogger(DevServicesArtemisJmsProcessor.class); +public class DevServicesArtemisProcessor { + private static final Logger LOGGER = Logger.getLogger(DevServicesArtemisProcessor.class); private static final String QUARKUS_ARTEMIS_URL = "quarkus.artemis.url"; /** @@ -164,7 +162,7 @@ private RunningDevService startArtemis(DockerStatusBuildItem dockerStatusBuildIt ConfigureUtil.configureSharedNetwork(container, "artemis"); if (config.serviceName != null) { - container.withLabel(DevServicesArtemisJmsProcessor.DEV_SERVICE_LABEL, config.serviceName); + container.withLabel(DevServicesArtemisProcessor.DEV_SERVICE_LABEL, config.serviceName); } timeout.ifPresent(container::withStartupTimeout); diff --git a/jms/deployment/pom.xml b/jms/deployment/pom.xml index b6b23c09..6eeead04 100644 --- a/jms/deployment/pom.xml +++ b/jms/deployment/pom.xml @@ -15,20 +15,6 @@ io.quarkiverse.artemis quarkus-artemis-core-deployment - - io.quarkus - quarkus-devservices-deployment - - - org.testcontainers - testcontainers - - - junit - junit - - - io.quarkiverse.artemis quarkus-artemis-jms From 202c6a074885a596110e934570c256b49ce451f3 Mon Sep 17 00:00:00 2001 From: Zheng Feng Date: Wed, 29 Jun 2022 09:57:18 +0800 Subject: [PATCH 3/5] update from Jacob's comments --- .../core/deployment/ArtemisDevServicesBuildTimeConfig.java | 4 +--- .../artemis/core/deployment/DevServicesArtemisProcessor.java | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/ArtemisDevServicesBuildTimeConfig.java b/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/ArtemisDevServicesBuildTimeConfig.java index b047a654..644bcbd0 100644 --- a/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/ArtemisDevServicesBuildTimeConfig.java +++ b/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/ArtemisDevServicesBuildTimeConfig.java @@ -9,9 +9,7 @@ public class ArtemisDevServicesBuildTimeConfig { /** - * If Dev Services for ActiveMQ Artemis has been explicitly enabled or disabled. Dev Services are generally enabled - * by default, unless there is an existing configuration present. For Artemis, Dev Services starts a broker unless - * {@code quarkus.artemis.url} is set. + * Enable or disable Dev Services explicitly. Dev Services are automatically enabled unless {@code quarkus.artemis.url} is set. */ @ConfigItem public Optional enabled = Optional.empty(); diff --git a/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/DevServicesArtemisProcessor.java b/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/DevServicesArtemisProcessor.java index d07d48f5..ee76ce23 100644 --- a/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/DevServicesArtemisProcessor.java +++ b/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/DevServicesArtemisProcessor.java @@ -82,7 +82,7 @@ public DevServicesResultBuildItem startArtemisDevService( } } catch (Throwable t) { compressor.closeAndDumpCaptured(); - throw new RuntimeException(t); + throw t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t); } if (devService == null) { @@ -171,7 +171,7 @@ private RunningDevService startArtemis(DockerStatusBuildItem dockerStatusBuildIt return new RunningDevService("ActiveMQ-Artemis", container.getContainerId(), container::close, - QUARKUS_ARTEMIS_URL, String.format("tcp://localhost:%d", container.getMappedPort(ARTEMIS_PORT))); + QUARKUS_ARTEMIS_URL, String.format("tcp://%s:%d", container.getHost(), container.getMappedPort(ARTEMIS_PORT))); }; return maybeContainerAddress From 236fc0d046fe669c91f1c2648f29fe610ab053a1 Mon Sep 17 00:00:00 2001 From: Zheng Feng Date: Wed, 29 Jun 2022 10:37:22 +0800 Subject: [PATCH 4/5] Add a dev service test in core integration test --- .../ArtemisDevServicesBuildTimeConfig.java | 11 +++++++- .../DevServicesArtemisProcessor.java | 8 +++--- .../pages/includes/quarkus-artemis-core.adoc | 11 +++++++- .../src/main/resources/application.properties | 2 +- .../io/quarkus/it/artemis/ArtemisHelper.java | 4 ++- .../it/artemis/ArtemisProducerTest.java | 20 ++------------- .../it/artemis/BaseArtemisProducerTest.java | 24 ++++++++++++++++++ .../it/artemis/DevServiceArtemisEnabled.java | 25 +++++++++++++++++++ .../DevServiceArtemisProducerTest.java | 16 ++++++++++++ 9 files changed, 96 insertions(+), 25 deletions(-) create mode 100644 integration-tests/core/src/test/java/io/quarkus/it/artemis/BaseArtemisProducerTest.java create mode 100644 integration-tests/core/src/test/java/io/quarkus/it/artemis/DevServiceArtemisEnabled.java create mode 100644 integration-tests/core/src/test/java/io/quarkus/it/artemis/DevServiceArtemisProducerTest.java diff --git a/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/ArtemisDevServicesBuildTimeConfig.java b/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/ArtemisDevServicesBuildTimeConfig.java index 644bcbd0..1c8c52cb 100644 --- a/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/ArtemisDevServicesBuildTimeConfig.java +++ b/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/ArtemisDevServicesBuildTimeConfig.java @@ -1,5 +1,7 @@ package io.quarkus.artemis.core.deployment; +import java.util.Collections; +import java.util.List; import java.util.Optional; import io.quarkus.runtime.annotations.ConfigGroup; @@ -9,7 +11,8 @@ public class ArtemisDevServicesBuildTimeConfig { /** - * Enable or disable Dev Services explicitly. Dev Services are automatically enabled unless {@code quarkus.artemis.url} is set. + * Enable or disable Dev Services explicitly. Dev Services are automatically enabled unless {@code quarkus.artemis.url} is + * set. */ @ConfigItem public Optional enabled = Optional.empty(); @@ -66,4 +69,10 @@ public class ArtemisDevServicesBuildTimeConfig { */ @ConfigItem(defaultValue = "admin") public String password; + + /** + * Queues to create on starting + */ + @ConfigItem(defaultValue = "[]") + public List queues = Collections.emptyList(); } diff --git a/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/DevServicesArtemisProcessor.java b/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/DevServicesArtemisProcessor.java index ee76ce23..e15fb7db 100644 --- a/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/DevServicesArtemisProcessor.java +++ b/core/deployment/src/main/java/io/quarkus/artemis/core/deployment/DevServicesArtemisProcessor.java @@ -157,6 +157,7 @@ private RunningDevService startArtemis(DockerStatusBuildItem dockerStatusBuildIt .withExposedPorts(ARTEMIS_PORT) .withEnv("AMQ_USER", config.user) .withEnv("AMQ_PASSWORD", config.password) + .withEnv("AMQ_EXTRA_ARGS", "--queues " + String.join(", ", config.queues)) .waitingFor(Wait.forLogMessage(".* Apache ActiveMQ Artemis Message Broker .*", 1)); ConfigureUtil.configureSharedNetwork(container, "artemis"); @@ -171,7 +172,8 @@ private RunningDevService startArtemis(DockerStatusBuildItem dockerStatusBuildIt return new RunningDevService("ActiveMQ-Artemis", container.getContainerId(), container::close, - QUARKUS_ARTEMIS_URL, String.format("tcp://%s:%d", container.getHost(), container.getMappedPort(ARTEMIS_PORT))); + QUARKUS_ARTEMIS_URL, + String.format("tcp://%s:%d", container.getHost(), container.getMappedPort(ARTEMIS_PORT))); }; return maybeContainerAddress @@ -193,10 +195,9 @@ private static final class ArtemisDevServiceCfg { private final Integer fixedExposedPort; private final boolean shared; private final String serviceName; - private final String user; - private final String password; + private final List queues; public ArtemisDevServiceCfg(ArtemisDevServicesBuildTimeConfig config) { this.devServicesEnabled = config.enabled.orElse(true); @@ -206,6 +207,7 @@ public ArtemisDevServiceCfg(ArtemisDevServicesBuildTimeConfig config) { this.serviceName = config.serviceName; this.user = config.user; this.password = config.password; + this.queues = config.queues; } @Override diff --git a/docs/modules/ROOT/pages/includes/quarkus-artemis-core.adoc b/docs/modules/ROOT/pages/includes/quarkus-artemis-core.adoc index a30830a6..71905f35 100644 --- a/docs/modules/ROOT/pages/includes/quarkus-artemis-core.adoc +++ b/docs/modules/ROOT/pages/includes/quarkus-artemis-core.adoc @@ -30,7 +30,7 @@ a|icon:lock[title=Fixed at build time] [[quarkus-artemis-core_quarkus.artemis.de [.description] -- -If Dev Services for ActiveMQ Artemis has been explicitly enabled or disabled. Dev Services are generally enabled by default, unless there is an existing configuration present. For Artemis, Dev Services starts a broker unless `quarkus.artemis.url` is set. +Enable or disable Dev Services explicitly. Dev Services are automatically enabled unless `quarkus.artemis.url` is set. --|boolean | @@ -93,6 +93,15 @@ Password to start artemis broker |`admin` +a|icon:lock[title=Fixed at build time] [[quarkus-artemis-core_quarkus.artemis.devservices.queues]]`link:#quarkus-artemis-core_quarkus.artemis.devservices.queues[quarkus.artemis.devservices.queues]` + +[.description] +-- +Queues to create on starting +--|list of string +|`[]` + + a| [[quarkus-artemis-core_quarkus.artemis.url]]`link:#quarkus-artemis-core_quarkus.artemis.url[quarkus.artemis.url]` [.description] diff --git a/integration-tests/core/src/main/resources/application.properties b/integration-tests/core/src/main/resources/application.properties index d7f1552f..6b27fee5 100644 --- a/integration-tests/core/src/main/resources/application.properties +++ b/integration-tests/core/src/main/resources/application.properties @@ -1 +1 @@ -quarkus.artemis.url=tcp://localhost:61616 +quarkus.artemis.devservices.enabled=false diff --git a/integration-tests/core/src/test/java/io/quarkus/it/artemis/ArtemisHelper.java b/integration-tests/core/src/test/java/io/quarkus/it/artemis/ArtemisHelper.java index da1b14a8..99fe1b2b 100644 --- a/integration-tests/core/src/test/java/io/quarkus/it/artemis/ArtemisHelper.java +++ b/integration-tests/core/src/test/java/io/quarkus/it/artemis/ArtemisHelper.java @@ -4,6 +4,7 @@ import org.apache.activemq.artemis.api.core.client.ActiveMQClient; import org.apache.activemq.artemis.api.core.client.ClientSession; +import org.eclipse.microprofile.config.ConfigProvider; public interface ArtemisHelper { @@ -12,6 +13,7 @@ default String createBody() { } default ClientSession createSession() throws Exception { - return ActiveMQClient.createServerLocator("tcp://localhost:61616").createSessionFactory().createSession(); + String url = ConfigProvider.getConfig().getValue("quarkus.artemis.url", String.class); + return ActiveMQClient.createServerLocator(url).createSessionFactory().createSession(); } } diff --git a/integration-tests/core/src/test/java/io/quarkus/it/artemis/ArtemisProducerTest.java b/integration-tests/core/src/test/java/io/quarkus/it/artemis/ArtemisProducerTest.java index 90e39a21..72372aa3 100644 --- a/integration-tests/core/src/test/java/io/quarkus/it/artemis/ArtemisProducerTest.java +++ b/integration-tests/core/src/test/java/io/quarkus/it/artemis/ArtemisProducerTest.java @@ -1,33 +1,17 @@ package io.quarkus.it.artemis; -import javax.ws.rs.core.Response.Status; - -import org.apache.activemq.artemis.api.core.client.ClientMessage; -import org.apache.activemq.artemis.api.core.client.ClientSession; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import io.quarkus.artemis.test.ArtemisTestResource; import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.junit.QuarkusTest; -import io.restassured.RestAssured; -import io.restassured.response.Response; @QuarkusTest @QuarkusTestResource(ArtemisTestResource.class) -public class ArtemisProducerTest implements ArtemisHelper { +public class ArtemisProducerTest extends BaseArtemisProducerTest { @Test public void test() throws Exception { - String body = createBody(); - Response response = RestAssured.with().body(body).post("/artemis"); - Assertions.assertEquals(Status.NO_CONTENT.getStatusCode(), response.statusCode()); - - try (ClientSession session = createSession()) { - session.start(); - ClientMessage message = session.createConsumer("test-core").receive(1000L); - message.acknowledge(); - Assertions.assertEquals(body, message.getBodyBuffer().readString()); - } + super.test(); } } diff --git a/integration-tests/core/src/test/java/io/quarkus/it/artemis/BaseArtemisProducerTest.java b/integration-tests/core/src/test/java/io/quarkus/it/artemis/BaseArtemisProducerTest.java new file mode 100644 index 00000000..425c4859 --- /dev/null +++ b/integration-tests/core/src/test/java/io/quarkus/it/artemis/BaseArtemisProducerTest.java @@ -0,0 +1,24 @@ +package io.quarkus.it.artemis; + +import org.apache.activemq.artemis.api.core.client.ClientMessage; +import org.apache.activemq.artemis.api.core.client.ClientSession; +import org.junit.jupiter.api.Assertions; + +import io.restassured.RestAssured; +import io.restassured.response.Response; + +public abstract class BaseArtemisProducerTest implements ArtemisHelper { + + public void test() throws Exception { + String body = createBody(); + Response response = RestAssured.with().body(body).post("/artemis"); + Assertions.assertEquals(javax.ws.rs.core.Response.Status.NO_CONTENT.getStatusCode(), response.statusCode()); + + try (ClientSession session = createSession()) { + session.start(); + ClientMessage message = session.createConsumer("test-core").receive(1000L); + message.acknowledge(); + Assertions.assertEquals(body, message.getBodyBuffer().readString()); + } + } +} diff --git a/integration-tests/core/src/test/java/io/quarkus/it/artemis/DevServiceArtemisEnabled.java b/integration-tests/core/src/test/java/io/quarkus/it/artemis/DevServiceArtemisEnabled.java new file mode 100644 index 00000000..a807459b --- /dev/null +++ b/integration-tests/core/src/test/java/io/quarkus/it/artemis/DevServiceArtemisEnabled.java @@ -0,0 +1,25 @@ +package io.quarkus.it.artemis; + +import java.util.HashMap; +import java.util.Map; + +import io.quarkus.test.junit.QuarkusTestProfile; + +public class DevServiceArtemisEnabled implements QuarkusTestProfile { + public DevServiceArtemisEnabled() { + } + + @Override + public Map getConfigOverrides() { + Map props = new HashMap<>(); + props.put("quarkus.artemis.devservices.enabled", "true"); + props.put("quarkus.artemis.devservices.queues", "test-core"); + + return props; + } + + @Override + public boolean disableGlobalTestResources() { + return true; + } +} diff --git a/integration-tests/core/src/test/java/io/quarkus/it/artemis/DevServiceArtemisProducerTest.java b/integration-tests/core/src/test/java/io/quarkus/it/artemis/DevServiceArtemisProducerTest.java new file mode 100644 index 00000000..3687695a --- /dev/null +++ b/integration-tests/core/src/test/java/io/quarkus/it/artemis/DevServiceArtemisProducerTest.java @@ -0,0 +1,16 @@ +package io.quarkus.it.artemis; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.TestProfile; + +@QuarkusTest +@TestProfile(DevServiceArtemisEnabled.class) +public class DevServiceArtemisProducerTest extends BaseArtemisProducerTest { + + @Test + public void test() throws Exception { + super.test(); + } +} From 19f4e18ec8e8fe82cc62e1eaedbca80f9bf8b3f0 Mon Sep 17 00:00:00 2001 From: Zheng Feng Date: Wed, 29 Jun 2022 10:53:37 +0800 Subject: [PATCH 5/5] update docs and mark ArtemisTestResource Deprecated --- docs/modules/ROOT/pages/index.adoc | 3 +++ .../main/java/io/quarkus/artemis/test/ArtemisTestResource.java | 1 + 2 files changed, 4 insertions(+) diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index cb5432dc..a32ba5a3 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -45,6 +45,9 @@ Once the BOM is added, simply add the `io.quarkiverse.artemis:quarkus-artemis-jm ---- +== Artemis DevServices +Artemis DevServices are automatically enabled unless `quarkus.artemis.url` is set or `quarkus.artemis.devservices.enabled` is *false* explicitly. And if you still want to use `ArtemisTestResource` in the test, you need to disable artemis devservices. + [[extension-configuration-reference]] == Configuration Reference diff --git a/test-framework/src/main/java/io/quarkus/artemis/test/ArtemisTestResource.java b/test-framework/src/main/java/io/quarkus/artemis/test/ArtemisTestResource.java index ca50bf51..faa3fb9a 100644 --- a/test-framework/src/main/java/io/quarkus/artemis/test/ArtemisTestResource.java +++ b/test-framework/src/main/java/io/quarkus/artemis/test/ArtemisTestResource.java @@ -10,6 +10,7 @@ import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; +@Deprecated public class ArtemisTestResource implements QuarkusTestResourceLifecycleManager { private EmbeddedActiveMQ embedded;