diff --git a/bom/application/pom.xml b/bom/application/pom.xml
index a8e24a8106ba3..831c4580688fd 100644
--- a/bom/application/pom.xml
+++ b/bom/application/pom.xml
@@ -639,6 +639,11 @@
quarkus-devservices-common
${project.version}
+
+ io.quarkus
+ quarkus-devservices-deployment
+ ${project.version}
+
io.quarkus
quarkus-elasticsearch-rest-client-common
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/DevServicesConfigResultBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/DevServicesConfigResultBuildItem.java
index 0d98a21d0c42e..675e755bbd291 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/DevServicesConfigResultBuildItem.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/DevServicesConfigResultBuildItem.java
@@ -8,7 +8,10 @@
* Used to start and configure dev services, any processor starting dev services should produce these items.
*
* Quarkus will make sure the relevant settings are present in both JVM and native modes.
+ *
+ * @deprecated use {@link DevServicesResultBuildItem}
*/
+@Deprecated(since = "https://github.com/quarkusio/quarkus/pull/23048")
public final class DevServicesConfigResultBuildItem extends MultiBuildItem {
final String key;
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/DevServicesResultBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/DevServicesResultBuildItem.java
new file mode 100644
index 0000000000000..5ed1385142e11
--- /dev/null
+++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/DevServicesResultBuildItem.java
@@ -0,0 +1,97 @@
+package io.quarkus.deployment.builditem;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import io.quarkus.builder.item.MultiBuildItem;
+
+/**
+ * BuildItem for running dev services
+ * Combines injected configs to the application with container id (if it exists).
+ *
+ * Processors are expected to return this build item not only when the dev service first starts,
+ * but also if a running dev service already exists.
+ *
+ * {@link RunningDevService} helps to manage the lifecycle of the running dev service
+ */
+public final class DevServicesResultBuildItem extends MultiBuildItem {
+
+ private final String name;
+ private final String containerId;
+ private final Map config;
+
+ public DevServicesResultBuildItem(String name, String containerId, Map config) {
+ this.name = name;
+ this.containerId = containerId;
+ this.config = config;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getContainerId() {
+ return containerId;
+ }
+
+ public Map getConfig() {
+ return config;
+ }
+
+ public static class RunningDevService implements Closeable {
+
+ private final String name;
+ private final String containerId;
+ private final Map config;
+ private final Closeable closeable;
+
+ private static Map mapOf(String key, String value) {
+ Map map = new HashMap<>();
+ map.put(key, value);
+ return map;
+ }
+
+ public RunningDevService(String name, String containerId, Closeable closeable, String key, String value) {
+ this(name, containerId, closeable, mapOf(key, value));
+ }
+
+ public RunningDevService(String name, String containerId, Closeable closeable, Map config) {
+ this.name = name;
+ this.containerId = containerId;
+ this.closeable = closeable;
+ this.config = Collections.unmodifiableMap(config);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getContainerId() {
+ return containerId;
+ }
+
+ public Map getConfig() {
+ return config;
+ }
+
+ public Closeable getCloseable() {
+ return closeable;
+ }
+
+ public boolean isOwner() {
+ return closeable != null;
+ }
+
+ @Override
+ public void close() throws IOException {
+ this.closeable.close();
+ }
+
+ public DevServicesResultBuildItem toBuildItem() {
+ return new DevServicesResultBuildItem(name, containerId, config);
+ }
+ }
+}
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/devservices/ContainerInfo.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/devservices/ContainerInfo.java
new file mode 100644
index 0000000000000..9227f0b1a06e1
--- /dev/null
+++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/devservices/ContainerInfo.java
@@ -0,0 +1,154 @@
+package io.quarkus.deployment.dev.devservices;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class ContainerInfo {
+
+ private String id;
+ private String[] names;
+ private String imageName;
+ private String status;
+ private String[] networks;
+ private Map labels;
+ private ContainerPort[] exposedPorts;
+
+ public ContainerInfo() {
+ }
+
+ public ContainerInfo(String id, String[] names, String imageName, String status, String[] networks,
+ Map labels, ContainerPort[] exposedPorts) {
+ this.id = id;
+ this.names = names;
+ this.imageName = imageName;
+ this.status = status;
+ this.networks = networks;
+ this.labels = labels;
+ this.exposedPorts = exposedPorts;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getShortId() {
+ return id.substring(0, 12);
+ }
+
+ public String[] getNames() {
+ return names;
+ }
+
+ public void setNames(String[] names) {
+ this.names = names;
+ }
+
+ public String formatNames() {
+ return String.join(",", getNames());
+ }
+
+ public String getImageName() {
+ return imageName;
+ }
+
+ public void setImageName(String imageName) {
+ this.imageName = imageName;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String[] getNetworks() {
+ return networks;
+ }
+
+ public void setNetworks(String[] networks) {
+ this.networks = networks;
+ }
+
+ public String formatNetworks() {
+ return String.join(",", getNetworks());
+ }
+
+ public Map getLabels() {
+ return labels;
+ }
+
+ public void setLabels(Map labels) {
+ this.labels = labels;
+ }
+
+ public ContainerPort[] getExposedPorts() {
+ return exposedPorts;
+ }
+
+ public void setExposedPorts(ContainerPort[] exposedPorts) {
+ this.exposedPorts = exposedPorts;
+ }
+
+ public String formatPorts() {
+ return Arrays.stream(getExposedPorts())
+ .filter(p -> p.getPublicPort() != null)
+ .map(c -> c.getIp() + ":" + c.getPublicPort() + "->" + c.getPrivatePort() + "/" + c.getType())
+ .collect(Collectors.joining(" ,"));
+ }
+
+ public static class ContainerPort {
+ private String ip;
+ private Integer privatePort;
+ private Integer publicPort;
+ private String type;
+
+ public ContainerPort() {
+ }
+
+ public ContainerPort(String ip, Integer privatePort, Integer publicPort, String type) {
+ this.ip = ip;
+ this.privatePort = privatePort;
+ this.publicPort = publicPort;
+ this.type = type;
+ }
+
+ public String getIp() {
+ return ip;
+ }
+
+ public void setIp(String ip) {
+ this.ip = ip;
+ }
+
+ public Integer getPrivatePort() {
+ return privatePort;
+ }
+
+ public void setPrivatePort(Integer privatePort) {
+ this.privatePort = privatePort;
+ }
+
+ public Integer getPublicPort() {
+ return publicPort;
+ }
+
+ public void setPublicPort(Integer publicPort) {
+ this.publicPort = publicPort;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+ }
+}
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/devservices/DevServiceDescriptionBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/devservices/DevServiceDescriptionBuildItem.java
new file mode 100644
index 0000000000000..9d9374a342c4c
--- /dev/null
+++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/devservices/DevServiceDescriptionBuildItem.java
@@ -0,0 +1,55 @@
+package io.quarkus.deployment.dev.devservices;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import io.quarkus.builder.item.MultiBuildItem;
+
+public final class DevServiceDescriptionBuildItem extends MultiBuildItem {
+ private String name;
+ private ContainerInfo containerInfo;
+ private Map configs;
+
+ public DevServiceDescriptionBuildItem() {
+ }
+
+ public DevServiceDescriptionBuildItem(String name, ContainerInfo containerInfo, Map configs) {
+ this.name = name;
+ this.containerInfo = containerInfo;
+ this.configs = configs;
+ }
+
+ public boolean hasContainerInfo() {
+ return containerInfo != null;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public ContainerInfo getContainerInfo() {
+ return containerInfo;
+ }
+
+ public Map getConfigs() {
+ return configs;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setContainerInfo(ContainerInfo containerInfo) {
+ this.containerInfo = containerInfo;
+ }
+
+ public void setConfigs(Map configs) {
+ this.configs = configs;
+ }
+
+ public String formatConfigs() {
+ return configs.entrySet().stream()
+ .map(e -> e.getKey() + "=" + e.getValue())
+ .collect(Collectors.joining(", "));
+ }
+}
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/DevServicesConfigBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/DevServicesConfigBuildStep.java
index 5c01b2b7bbf1b..c035e78d5e324 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/steps/DevServicesConfigBuildStep.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/DevServicesConfigBuildStep.java
@@ -16,6 +16,7 @@
import io.quarkus.deployment.builditem.DevServicesConfigResultBuildItem;
import io.quarkus.deployment.builditem.DevServicesLauncherConfigResultBuildItem;
import io.quarkus.deployment.builditem.DevServicesNativeConfigResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem;
import io.quarkus.deployment.builditem.RunTimeConfigurationDefaultBuildItem;
import io.quarkus.deployment.builditem.ServiceStartBuildItem;
@@ -32,9 +33,13 @@ List deprecated(List runtimeConfig,
List devServicesConfigResultBuildItems,
+ List devServicesResultBuildItems,
CuratedApplicationShutdownBuildItem shutdownBuildItem) {
Map newProperties = new HashMap<>(devServicesConfigResultBuildItems.stream().collect(
Collectors.toMap(DevServicesConfigResultBuildItem::getKey, DevServicesConfigResultBuildItem::getValue)));
+ for (DevServicesResultBuildItem resultBuildItem : devServicesResultBuildItems) {
+ newProperties.putAll(resultBuildItem.getConfig());
+ }
Config config = ConfigProvider.getConfig();
//check if there are existing already started dev services
//if there were no changes to the processors they don't produce config
diff --git a/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/DevServicesLambdaProcessor.java b/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/DevServicesLambdaProcessor.java
index 8a8eaea1e625a..a6e717def63cc 100644
--- a/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/DevServicesLambdaProcessor.java
+++ b/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/DevServicesLambdaProcessor.java
@@ -2,6 +2,8 @@
import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
@@ -11,12 +13,13 @@
import io.quarkus.amazon.lambda.runtime.LambdaHotReplacementRecorder;
import io.quarkus.amazon.lambda.runtime.MockEventServer;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
+import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Produce;
import io.quarkus.deployment.annotations.Record;
-import io.quarkus.deployment.builditem.DevServicesConfigResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.builditem.RuntimeApplicationShutdownBuildItem;
import io.quarkus.deployment.builditem.ServiceStartBuildItem;
@@ -54,7 +57,7 @@ private boolean legacyTestingEnabled() {
public void startEventServer(LaunchModeBuildItem launchMode,
LambdaConfig config,
Optional override,
- BuildProducer devServicePropertiesProducer,
+ BuildProducer devServicePropertiesProducer,
BuildProducer runtimeApplicationShutdownBuildItemBuildProducer)
throws Exception {
if (!launchMode.getLaunchMode().isDevOrTest())
@@ -77,8 +80,10 @@ public void startEventServer(LaunchModeBuildItem launchMode,
startMode = launchMode.getLaunchMode();
server.start(port);
String baseUrl = "localhost:" + port + MockEventServer.BASE_PATH;
+ Map properties = new HashMap<>();
+ properties.put(AmazonLambdaApi.QUARKUS_INTERNAL_AWS_LAMBDA_TEST_API, baseUrl);
devServicePropertiesProducer.produce(
- new DevServicesConfigResultBuildItem(AmazonLambdaApi.QUARKUS_INTERNAL_AWS_LAMBDA_TEST_API, baseUrl));
+ new DevServicesResultBuildItem(Feature.AMAZON_LAMBDA.getName(), null, properties));
Runnable closeTask = () -> {
if (server != null) {
try {
diff --git a/extensions/apicurio-registry-avro/deployment/pom.xml b/extensions/apicurio-registry-avro/deployment/pom.xml
index 388a8ad5b82fb..4df4d4cbfd532 100644
--- a/extensions/apicurio-registry-avro/deployment/pom.xml
+++ b/extensions/apicurio-registry-avro/deployment/pom.xml
@@ -49,7 +49,7 @@
io.quarkus
- quarkus-devservices-common
+ quarkus-devservices-deployment
diff --git a/extensions/apicurio-registry-avro/deployment/src/main/java/io/quarkus/apicurio/registry/avro/DevServicesApicurioRegistryProcessor.java b/extensions/apicurio-registry-avro/deployment/src/main/java/io/quarkus/apicurio/registry/avro/DevServicesApicurioRegistryProcessor.java
index ae450b9c24993..6bcf7a83f93e7 100644
--- a/extensions/apicurio-registry-avro/deployment/src/main/java/io/quarkus/apicurio/registry/avro/DevServicesApicurioRegistryProcessor.java
+++ b/extensions/apicurio-registry-avro/deployment/src/main/java/io/quarkus/apicurio/registry/avro/DevServicesApicurioRegistryProcessor.java
@@ -11,12 +11,13 @@
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.DockerImageName;
+import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsDockerWorking;
import io.quarkus.deployment.IsNormal;
-import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.CuratedApplicationShutdownBuildItem;
-import io.quarkus.deployment.builditem.DevServicesConfigResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem.RunningDevService;
import io.quarkus.deployment.builditem.DevServicesSharedNetworkBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.console.ConsoleInstalledBuildItem;
@@ -50,56 +51,50 @@ public class DevServicesApicurioRegistryProcessor {
private static final ContainerLocator apicurioRegistryContainerLocator = new ContainerLocator(DEV_SERVICE_LABEL,
APICURIO_REGISTRY_PORT);
- static volatile AutoCloseable closeable;
+ static volatile RunningDevService devService;
static volatile ApicurioRegistryDevServiceCfg cfg;
static volatile boolean first = true;
private final IsDockerWorking isDockerWorking = new IsDockerWorking(true);
@BuildStep(onlyIfNot = IsNormal.class, onlyIf = GlobalDevServicesConfig.Enabled.class)
- public void startApicurioRegistryDevService(LaunchModeBuildItem launchMode,
+ public DevServicesResultBuildItem startApicurioRegistryDevService(LaunchModeBuildItem launchMode,
ApicurioRegistryDevServicesBuildTimeConfig apicurioRegistryDevServices,
List devServicesSharedNetworkBuildItem,
- BuildProducer devServicesConfiguration,
Optional consoleInstalledBuildItem,
CuratedApplicationShutdownBuildItem closeBuildItem,
LoggingSetupBuildItem loggingSetupBuildItem, GlobalDevServicesConfig devServicesConfig) {
ApicurioRegistryDevServiceCfg configuration = getConfiguration(apicurioRegistryDevServices);
- if (closeable != null) {
+ if (devService != null) {
boolean restartRequired = !configuration.equals(cfg);
if (!restartRequired) {
- return;
+ return devService.toBuildItem();
}
shutdownApicurioRegistry();
cfg = null;
}
- ApicurioRegistry apicurioRegistry;
StartupLogCompressor compressor = new StartupLogCompressor(
(launchMode.isTest() ? "(test) " : "") + "Apicurio Registry Dev Services Starting:",
consoleInstalledBuildItem, loggingSetupBuildItem);
try {
- apicurioRegistry = startApicurioRegistry(configuration, launchMode,
+ devService = startApicurioRegistry(configuration, launchMode,
!devServicesSharedNetworkBuildItem.isEmpty(), devServicesConfig.timeout);
- if (apicurioRegistry == null) {
- compressor.close();
- return;
- }
compressor.close();
} catch (Throwable t) {
compressor.closeAndDumpCaptured();
throw new RuntimeException(t);
}
- cfg = configuration;
- closeable = apicurioRegistry.getCloseable();
+ if (devService == null) {
+ return null;
+ }
- devServicesConfiguration.produce(new DevServicesConfigResultBuildItem(
- REGISTRY_URL_CONFIG, apicurioRegistry.getUrl() + "/apis/registry/v2"));
+ cfg = configuration;
- if (apicurioRegistry.isOwner()) {
- log.infof("Dev Services for Apicurio Registry started. The registry is available at %s", apicurioRegistry.getUrl());
+ if (devService.isOwner()) {
+ log.infof("Dev Services for Apicurio Registry started. The registry is available at %s", getRegistryUrlConfig());
}
// Configure the watch dog
@@ -108,31 +103,36 @@ public void startApicurioRegistryDevService(LaunchModeBuildItem launchMode,
Runnable closeTask = new Runnable() {
@Override
public void run() {
- if (closeable != null) {
+ if (devService != null) {
shutdownApicurioRegistry();
}
first = true;
- closeable = null;
+ devService = null;
cfg = null;
}
};
closeBuildItem.addCloseTask(closeTask, true);
}
+ return devService.toBuildItem();
+ }
+
+ private String getRegistryUrlConfig() {
+ return devService.getConfig().get(REGISTRY_URL_CONFIG) + "/apis/registry/v2";
}
private void shutdownApicurioRegistry() {
- if (closeable != null) {
+ if (devService != null) {
try {
- closeable.close();
+ devService.close();
} catch (Throwable e) {
log.error("Failed to stop Apicurio Registry", e);
} finally {
- closeable = null;
+ devService = null;
}
}
}
- private ApicurioRegistry startApicurioRegistry(ApicurioRegistryDevServiceCfg config, LaunchModeBuildItem launchMode,
+ private RunningDevService startApicurioRegistry(ApicurioRegistryDevServiceCfg config, LaunchModeBuildItem launchMode,
boolean useSharedNetwork, Optional timeout) {
if (!config.devServicesEnabled) {
// explicitly disabled
@@ -157,7 +157,8 @@ private ApicurioRegistry startApicurioRegistry(ApicurioRegistryDevServiceCfg con
// Starting the broker
return apicurioRegistryContainerLocator.locateContainer(config.serviceName, config.shared, launchMode.getLaunchMode())
- .map(containerAddress -> new ApicurioRegistry(containerAddress.getUrl(), null))
+ .map(containerAddress -> new RunningDevService(Feature.APICURIO_REGISTRY_AVRO.getName(),
+ containerAddress.getId(), null, REGISTRY_URL_CONFIG, containerAddress.getUrl()))
.orElseGet(() -> {
ApicurioRegistryContainer container = new ApicurioRegistryContainer(
DockerImageName.parse(config.imageName), config.fixedExposedPort,
@@ -166,7 +167,8 @@ private ApicurioRegistry startApicurioRegistry(ApicurioRegistryDevServiceCfg con
timeout.ifPresent(container::withStartupTimeout);
container.start();
- return new ApicurioRegistry(container.getUrl(), container);
+ return new RunningDevService(Feature.APICURIO_REGISTRY_AVRO.getName(), container.getContainerId(),
+ container::close, REGISTRY_URL_CONFIG, container.getUrl());
});
}
@@ -193,28 +195,6 @@ private ApicurioRegistryDevServiceCfg getConfiguration(ApicurioRegistryDevServic
return new ApicurioRegistryDevServiceCfg(cfg);
}
- private static class ApicurioRegistry {
- private final String url;
- private final AutoCloseable closeable;
-
- public ApicurioRegistry(String url, AutoCloseable closeable) {
- this.url = url;
- this.closeable = closeable;
- }
-
- public String getUrl() {
- return url;
- }
-
- public AutoCloseable getCloseable() {
- return closeable;
- }
-
- public boolean isOwner() {
- return closeable != null;
- }
- }
-
private static final class ApicurioRegistryDevServiceCfg {
private final boolean devServicesEnabled;
private final String imageName;
diff --git a/extensions/apicurio-registry-avro/runtime/pom.xml b/extensions/apicurio-registry-avro/runtime/pom.xml
index 8544a4c7a3069..7f4be7fd6dbc6 100644
--- a/extensions/apicurio-registry-avro/runtime/pom.xml
+++ b/extensions/apicurio-registry-avro/runtime/pom.xml
@@ -46,11 +46,10 @@
io.quarkus
quarkus-vertx
-
-
- org.apache.commons
- commons-lang3
-
+
+ org.apache.commons
+ commons-lang3
+
diff --git a/extensions/datasource/deployment-spi/src/main/java/io/quarkus/datasource/deployment/spi/DevServicesDatasourceProvider.java b/extensions/datasource/deployment-spi/src/main/java/io/quarkus/datasource/deployment/spi/DevServicesDatasourceProvider.java
index fe5666027f196..25bd51c5571ed 100644
--- a/extensions/datasource/deployment-spi/src/main/java/io/quarkus/datasource/deployment/spi/DevServicesDatasourceProvider.java
+++ b/extensions/datasource/deployment-spi/src/main/java/io/quarkus/datasource/deployment/spi/DevServicesDatasourceProvider.java
@@ -23,18 +23,24 @@ default boolean isDockerRequired() {
class RunningDevServicesDatasource {
+ private final String id;
private final String url;
private final String username;
private final String password;
private final Closeable closeTask;
- public RunningDevServicesDatasource(String url, String username, String password, Closeable closeTask) {
+ public RunningDevServicesDatasource(String id, String url, String username, String password, Closeable closeTask) {
+ this.id = id;
this.url = url;
this.username = username;
this.password = password;
this.closeTask = closeTask;
}
+ public String getId() {
+ return id;
+ }
+
public String getUrl() {
return url;
}
diff --git a/extensions/datasource/deployment/pom.xml b/extensions/datasource/deployment/pom.xml
index 3cae04a4341ce..676f7b24bed92 100644
--- a/extensions/datasource/deployment/pom.xml
+++ b/extensions/datasource/deployment/pom.xml
@@ -33,6 +33,10 @@
io.quarkus
quarkus-kubernetes-service-binding-spi
+
+ io.quarkus
+ quarkus-devservices-deployment
+
diff --git a/extensions/datasource/deployment/src/main/java/io/quarkus/datasource/deployment/devservices/DevServicesDatasourceProcessor.java b/extensions/datasource/deployment/src/main/java/io/quarkus/datasource/deployment/devservices/DevServicesDatasourceProcessor.java
index c5f746cbaa685..cb85cc34041f3 100644
--- a/extensions/datasource/deployment/src/main/java/io/quarkus/datasource/deployment/devservices/DevServicesDatasourceProcessor.java
+++ b/extensions/datasource/deployment/src/main/java/io/quarkus/datasource/deployment/devservices/DevServicesDatasourceProcessor.java
@@ -25,7 +25,8 @@
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.CuratedApplicationShutdownBuildItem;
-import io.quarkus.deployment.builditem.DevServicesConfigResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem.RunningDevService;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.console.ConsoleInstalledBuildItem;
import io.quarkus.deployment.console.StartupLogCompressor;
@@ -38,7 +39,7 @@ public class DevServicesDatasourceProcessor {
private static final Logger log = Logger.getLogger(DevServicesDatasourceProcessor.class);
- static volatile List databases;
+ static volatile List databases;
static volatile Map cachedProperties;
@@ -53,7 +54,7 @@ DevServicesDatasourceResultBuildItem launchDatabases(CurateOutcomeBuildItem cura
DataSourcesBuildTimeConfig dataSourceBuildTimeConfig,
LaunchModeBuildItem launchMode,
List configurationHandlerBuildItems,
- BuildProducer devServicesResultBuildItemBuildProducer,
+ BuildProducer devServicesResultBuildItemBuildProducer,
Optional consoleInstalledBuildItem,
CuratedApplicationShutdownBuildItem closeBuildItem,
LoggingSetupBuildItem loggingSetupBuildItem,
@@ -82,6 +83,10 @@ DevServicesDatasourceResultBuildItem launchDatabases(CurateOutcomeBuildItem cura
}
}
if (!restartRequired) {
+ for (RunningDevService database : databases) {
+ devServicesResultBuildItemBuildProducer.produce(database.toBuildItem());
+ }
+ // keep the previous behaviour of producing DevServicesDatasourceResultBuildItem only when the devservice first starts.
return null;
}
for (Closeable i : databases) {
@@ -104,7 +109,7 @@ DevServicesDatasourceResultBuildItem launchDatabases(CurateOutcomeBuildItem cura
//support for named datasources will come later
Map propertiesMap = new HashMap<>();
- List closeableList = new ArrayList<>();
+ List runningDevServices = new ArrayList<>();
Map> configHandlersByDbType = configurationHandlerBuildItems
.stream()
.collect(Collectors.toMap(DevServicesDatasourceConfigurationHandlerBuildItem::getDbKind,
@@ -118,34 +123,26 @@ DevServicesDatasourceResultBuildItem launchDatabases(CurateOutcomeBuildItem cura
Map devDBProviderMap = devDBProviders.stream()
.collect(Collectors.toMap(DevServicesDatasourceProviderBuildItem::getDatabase,
DevServicesDatasourceProviderBuildItem::getDevServicesProvider));
- defaultResult = startDevDb(null, curateOutcomeBuildItem, installedDrivers,
+ RunningDevService defaultDevService = startDevDb(null, curateOutcomeBuildItem, installedDrivers,
!dataSourceBuildTimeConfig.namedDataSources.isEmpty(),
devDBProviderMap,
dataSourceBuildTimeConfig.defaultDataSource,
- configHandlersByDbType, propertiesMap, closeableList, launchMode.getLaunchMode(), consoleInstalledBuildItem,
+ configHandlersByDbType, propertiesMap, launchMode.getLaunchMode(), consoleInstalledBuildItem,
loggingSetupBuildItem, globalDevServicesConfig);
- List dbConfig = new ArrayList<>();
- if (defaultResult != null) {
- for (Map.Entry i : defaultResult.getConfigProperties().entrySet()) {
- dbConfig.add(new DevServicesConfigResultBuildItem(i.getKey(), i.getValue()));
- }
+ if (defaultDevService != null) {
+ runningDevServices.add(defaultDevService);
}
+ defaultResult = toDbResult(defaultDevService);
for (Map.Entry entry : dataSourceBuildTimeConfig.namedDataSources.entrySet()) {
- DevServicesDatasourceResultBuildItem.DbResult result = startDevDb(entry.getKey(), curateOutcomeBuildItem,
+ RunningDevService namedDevService = startDevDb(entry.getKey(), curateOutcomeBuildItem,
installedDrivers, true,
- devDBProviderMap, entry.getValue(), configHandlersByDbType, propertiesMap, closeableList,
+ devDBProviderMap, entry.getValue(), configHandlersByDbType, propertiesMap,
launchMode.getLaunchMode(), consoleInstalledBuildItem, loggingSetupBuildItem, globalDevServicesConfig);
- if (result != null) {
- namedResults.put(entry.getKey(), result);
- for (Map.Entry i : result.getConfigProperties().entrySet()) {
- dbConfig.add(new DevServicesConfigResultBuildItem(i.getKey(), i.getValue()));
- }
+ if (namedDevService != null) {
+ runningDevServices.add(namedDevService);
+ namedResults.put(entry.getKey(), toDbResult(namedDevService));
}
}
- for (DevServicesConfigResultBuildItem i : dbConfig) {
- devServicesResultBuildItemBuildProducer
- .produce(i);
- }
if (first) {
first = false;
@@ -168,8 +165,11 @@ public void run() {
};
closeBuildItem.addCloseTask(closeTask, true);
}
- databases = closeableList;
+ databases = runningDevServices;
cachedProperties = propertiesMap;
+ for (RunningDevService database : databases) {
+ devServicesResultBuildItemBuildProducer.produce(database.toBuildItem());
+ }
return new DevServicesDatasourceResultBuildItem(defaultResult, namedResults);
}
@@ -180,13 +180,13 @@ private String trim(String optional) {
return optional.trim();
}
- private DevServicesDatasourceResultBuildItem.DbResult startDevDb(String dbName,
+ private RunningDevService startDevDb(String dbName,
CurateOutcomeBuildItem curateOutcomeBuildItem,
List installedDrivers,
boolean hasNamedDatasources,
Map devDBProviders, DataSourceBuildTimeConfig dataSourceBuildTimeConfig,
Map> configurationHandlerBuildItems,
- Map propertiesMap, List closeableList,
+ Map propertiesMap,
LaunchMode launchMode, Optional consoleInstalledBuildItem,
LoggingSetupBuildItem loggingSetupBuildItem, GlobalDevServicesConfig globalDevServicesConfig) {
boolean explicitlyDisabled = !(dataSourceBuildTimeConfig.devservices.enabled.orElse(true));
@@ -266,7 +266,6 @@ private DevServicesDatasourceResultBuildItem.DbResult startDevDb(String dbName,
dataSourceBuildTimeConfig.devservices.containerProperties,
dataSourceBuildTimeConfig.devservices.properties,
dataSourceBuildTimeConfig.devservices.port, launchMode, globalDevServicesConfig.timeout);
- closeableList.add(datasource.getCloseTask());
propertiesMap.put(prefix + "db-kind", dataSourceBuildTimeConfig.dbKind.orElse(null));
String devServicesPrefix = prefix + "devservices.";
@@ -305,10 +304,17 @@ private DevServicesDatasourceResultBuildItem.DbResult startDevDb(String dbName,
devDebProperties.put(name, ConfigProvider.getConfig().getValue(name, String.class));
}
}
- return new DevServicesDatasourceResultBuildItem.DbResult(defaultDbKind.get(), devDebProperties);
+ return new RunningDevService(defaultDbKind.get(), datasource.getId(), datasource.getCloseTask(), devDebProperties);
} catch (Throwable t) {
compressor.closeAndDumpCaptured();
throw new RuntimeException(t);
}
}
+
+ private DevServicesDatasourceResultBuildItem.DbResult toDbResult(RunningDevService devService) {
+ if (devService == null) {
+ return null;
+ }
+ return new DevServicesDatasourceResultBuildItem.DbResult(devService.getName(), devService.getConfig());
+ }
}
diff --git a/extensions/devservices/common/src/main/java/io/quarkus/devservices/common/ContainerAddress.java b/extensions/devservices/common/src/main/java/io/quarkus/devservices/common/ContainerAddress.java
index 3d2f90d307bd5..2773132dfc9fc 100644
--- a/extensions/devservices/common/src/main/java/io/quarkus/devservices/common/ContainerAddress.java
+++ b/extensions/devservices/common/src/main/java/io/quarkus/devservices/common/ContainerAddress.java
@@ -1,14 +1,20 @@
package io.quarkus.devservices.common;
public class ContainerAddress {
+ private final String id;
private final String host;
private final int port;
- public ContainerAddress(String host, int port) {
+ public ContainerAddress(String id, String host, int port) {
+ this.id = id;
this.host = host;
this.port = port;
}
+ public String getId() {
+ return id;
+ }
+
public String getHost() {
return host;
}
diff --git a/extensions/devservices/common/src/main/java/io/quarkus/devservices/common/ContainerLocator.java b/extensions/devservices/common/src/main/java/io/quarkus/devservices/common/ContainerLocator.java
index 70de1108b2b5d..5ac10a7408e58 100644
--- a/extensions/devservices/common/src/main/java/io/quarkus/devservices/common/ContainerLocator.java
+++ b/extensions/devservices/common/src/main/java/io/quarkus/devservices/common/ContainerLocator.java
@@ -47,6 +47,7 @@ public Optional locateContainer(String serviceName, boolean sh
.flatMap(containerPort -> Optional.ofNullable(containerPort.getPublicPort())
.map(port -> {
final ContainerAddress containerAddress = new ContainerAddress(
+ container.getId(),
DockerClientFactory.instance().dockerHostIpAddress(),
containerPort.getPublicPort());
log.infof("Dev Services container found: %s (%s). Connecting to: %s.",
diff --git a/extensions/devservices/db2/src/main/java/io/quarkus/devservices/db2/deployment/DB2DevServicesProcessor.java b/extensions/devservices/db2/src/main/java/io/quarkus/devservices/db2/deployment/DB2DevServicesProcessor.java
index 3d1a02510b3a5..6fb9218f41c2a 100644
--- a/extensions/devservices/db2/src/main/java/io/quarkus/devservices/db2/deployment/DB2DevServicesProcessor.java
+++ b/extensions/devservices/db2/src/main/java/io/quarkus/devservices/db2/deployment/DB2DevServicesProcessor.java
@@ -44,7 +44,9 @@ public RunningDevServicesDatasource startDatabase(Optional username, Opt
LOG.info("Dev Services for IBM Db2 started.");
- return new RunningDevServicesDatasource(container.getEffectiveJdbcUrl(), container.getUsername(),
+ return new RunningDevServicesDatasource(container.getContainerId(),
+ container.getEffectiveJdbcUrl(),
+ container.getUsername(),
container.getPassword(),
new Closeable() {
@Override
diff --git a/extensions/devservices/deployment/pom.xml b/extensions/devservices/deployment/pom.xml
new file mode 100644
index 0000000000000..f78183ff0248b
--- /dev/null
+++ b/extensions/devservices/deployment/pom.xml
@@ -0,0 +1,45 @@
+
+
+
+ quarkus-devservices-parent
+ io.quarkus
+ 999-SNAPSHOT
+
+ 4.0.0
+
+ quarkus-devservices-deployment
+ Quarkus - DevServices - Deployment
+
+
+
+ io.quarkus
+ quarkus-devservices-common
+
+
+ io.quarkus
+ quarkus-arc-deployment
+
+
+ io.quarkus
+ quarkus-core-deployment
+
+
+
+
+
+ maven-compiler-plugin
+
+
+
+ io.quarkus
+ quarkus-extension-processor
+ ${project.version}
+
+
+
+
+
+
+
diff --git a/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/ContainerLogForwarder.java b/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/ContainerLogForwarder.java
new file mode 100644
index 0000000000000..a7c366c9772b8
--- /dev/null
+++ b/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/ContainerLogForwarder.java
@@ -0,0 +1,79 @@
+package io.quarkus.devservices.deployment;
+
+import static org.testcontainers.containers.output.OutputFrame.OutputType.STDERR;
+import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT;
+
+import java.io.Closeable;
+import java.time.Instant;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.jboss.logging.Logger;
+import org.testcontainers.DockerClientFactory;
+import org.testcontainers.containers.output.FrameConsumerResultCallback;
+import org.testcontainers.containers.output.OutputFrame;
+
+import io.quarkus.deployment.dev.devservices.DevServiceDescriptionBuildItem;
+
+public class ContainerLogForwarder implements Closeable {
+
+ private final DevServiceDescriptionBuildItem devService;
+ private final AtomicLong timestamp = new AtomicLong(0L);
+ private final Logger logger;
+ private final String shortId;
+ private FrameConsumerResultCallback resultCallback;
+ private final AtomicBoolean running = new AtomicBoolean(false);
+
+ public ContainerLogForwarder(DevServiceDescriptionBuildItem devService) {
+ this.devService = devService;
+ this.logger = Logger.getLogger(devService.getName());
+ this.shortId = devService.getContainerInfo().getShortId();
+ }
+
+ public DevServiceDescriptionBuildItem getDevService() {
+ return devService;
+ }
+
+ public boolean isRunning() {
+ return running.get();
+ }
+
+ public void start() {
+ if (running.compareAndSet(false, true)) {
+ this.resultCallback = new FrameConsumerResultCallback();
+ this.resultCallback.addConsumer(STDOUT, frame -> {
+ if (running.get())
+ logger.infof("[%s] %s", shortId, updateTimestamp(frame));
+ });
+ this.resultCallback.addConsumer(STDERR, frame -> {
+ if (running.get())
+ logger.errorf("[%s] %s", shortId, updateTimestamp(frame));
+ });
+ DockerClientFactory.lazyClient().logContainerCmd(devService.getContainerInfo().getId())
+ .withFollowStream(true)
+ .withStdErr(true)
+ .withStdOut(true)
+ .withSince(timestamp.intValue())
+ .exec(resultCallback);
+ }
+ }
+
+ @Override
+ public void close() {
+ if (running.compareAndSet(true, false)) {
+ try {
+ resultCallback.close();
+ } catch (Throwable throwable) {
+ logger.errorf("Failed to close log forwarder %s", devService.getName());
+ } finally {
+ resultCallback = null;
+ }
+ }
+ }
+
+ private String updateTimestamp(OutputFrame frame) {
+ timestamp.set(Instant.now().getEpochSecond());
+ return frame.getUtf8String().trim();
+ }
+
+}
diff --git a/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/DevServicesCommand.java b/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/DevServicesCommand.java
new file mode 100644
index 0000000000000..03546bfe4fad2
--- /dev/null
+++ b/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/DevServicesCommand.java
@@ -0,0 +1,51 @@
+package io.quarkus.devservices.deployment;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.aesh.command.Command;
+import org.aesh.command.CommandResult;
+import org.aesh.command.GroupCommand;
+import org.aesh.command.GroupCommandDefinition;
+import org.aesh.command.invocation.CommandInvocation;
+
+import io.quarkus.deployment.console.SetCompleter;
+import io.quarkus.deployment.dev.devservices.DevServiceDescriptionBuildItem;
+
+@GroupCommandDefinition(name = "devservices", description = "Dev Service Commands")
+public class DevServicesCommand implements GroupCommand {
+ static List serviceDescriptions;
+
+ public DevServicesCommand(List serviceDescriptions) {
+ DevServicesCommand.serviceDescriptions = serviceDescriptions;
+ }
+
+ @Override
+ public List getCommands() {
+ return List.of(new DevServicesListCommand(), new DevServicesLogsCommand());
+ }
+
+ @Override
+ public CommandResult execute(CommandInvocation commandInvocation) {
+ commandInvocation.println(commandInvocation.getHelpInfo());
+ return CommandResult.SUCCESS;
+ }
+
+ static Optional findDevService(String devServiceName) {
+ return serviceDescriptions.stream()
+ .filter(d -> d.getName().equals(devServiceName))
+ .findFirst();
+ }
+
+ public static class DevServiceCompleter extends SetCompleter {
+
+ @Override
+ protected Set allOptions(String soFar) {
+ return serviceDescriptions.stream()
+ .map(DevServiceDescriptionBuildItem::getName)
+ .collect(Collectors.toSet());
+ }
+ }
+}
diff --git a/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/DevServicesListCommand.java b/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/DevServicesListCommand.java
new file mode 100644
index 0000000000000..56f142ff343ac
--- /dev/null
+++ b/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/DevServicesListCommand.java
@@ -0,0 +1,26 @@
+package io.quarkus.devservices.deployment;
+
+import static io.quarkus.devservices.deployment.DevServicesProcessor.printDevService;
+
+import org.aesh.command.Command;
+import org.aesh.command.CommandDefinition;
+import org.aesh.command.CommandResult;
+import org.aesh.command.invocation.CommandInvocation;
+
+import io.quarkus.deployment.dev.devservices.DevServiceDescriptionBuildItem;
+
+@CommandDefinition(name = "list", description = "List of dev services")
+public class DevServicesListCommand implements Command {
+
+ @Override
+ public CommandResult execute(CommandInvocation commandInvocation) {
+ commandInvocation.println("");
+ StringBuilder builder = new StringBuilder();
+ for (DevServiceDescriptionBuildItem serviceDescription : DevServicesCommand.serviceDescriptions) {
+ printDevService(builder, serviceDescription, false);
+ builder.append("\n");
+ }
+ commandInvocation.print(builder.toString());
+ return CommandResult.SUCCESS;
+ }
+}
diff --git a/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/DevServicesLogsCommand.java b/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/DevServicesLogsCommand.java
new file mode 100644
index 0000000000000..de75cd0cdbca2
--- /dev/null
+++ b/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/DevServicesLogsCommand.java
@@ -0,0 +1,67 @@
+package io.quarkus.devservices.deployment;
+
+import static io.quarkus.devservices.deployment.DevServicesCommand.findDevService;
+
+import java.io.IOException;
+import java.util.Optional;
+
+import org.aesh.command.Command;
+import org.aesh.command.CommandDefinition;
+import org.aesh.command.CommandResult;
+import org.aesh.command.invocation.CommandInvocation;
+import org.aesh.command.option.Argument;
+import org.aesh.command.option.Option;
+import org.testcontainers.DockerClientFactory;
+import org.testcontainers.containers.output.FrameConsumerResultCallback;
+import org.testcontainers.containers.output.OutputFrame;
+
+import com.github.dockerjava.api.command.LogContainerCmd;
+
+import io.quarkus.deployment.dev.devservices.DevServiceDescriptionBuildItem;
+import io.quarkus.devservices.deployment.DevServicesCommand.DevServiceCompleter;
+
+@CommandDefinition(name = "logs", description = "Print container logs")
+public class DevServicesLogsCommand implements Command {
+
+ @Argument(required = true, description = "Dev Service name", completer = DevServiceCompleter.class)
+ private String devService;
+
+ @Option(name = "follow", shortName = 'f', description = "Follow container logs", hasValue = false, defaultValue = "false")
+ private boolean follow;
+
+ @Option(name = "tail", shortName = 't', description = "Tail container logs", defaultValue = "-1")
+ private int tail;
+
+ @Override
+ public CommandResult execute(CommandInvocation commandInvocation) {
+ Optional devService = findDevService(this.devService);
+ if (devService.isPresent()) {
+ DevServiceDescriptionBuildItem desc = devService.get();
+ try (FrameConsumerResultCallback resultCallback = new FrameConsumerResultCallback()) {
+ resultCallback.addConsumer(OutputFrame.OutputType.STDERR,
+ frame -> commandInvocation.print(frame.getUtf8String()));
+ resultCallback.addConsumer(OutputFrame.OutputType.STDOUT,
+ frame -> commandInvocation.print(frame.getUtf8String()));
+ LogContainerCmd logCmd = DockerClientFactory.lazyClient()
+ .logContainerCmd(desc.getContainerInfo().getId())
+ .withFollowStream(follow)
+ .withTail(tail)
+ .withStdErr(true)
+ .withStdOut(true);
+ logCmd.exec(resultCallback);
+
+ if (follow) {
+ commandInvocation.inputLine();
+ } else {
+ resultCallback.awaitCompletion();
+ }
+ } catch (InterruptedException | IOException e) {
+ // noop
+ }
+ return CommandResult.SUCCESS;
+ } else {
+ commandInvocation.println("Could not find dev service with name " + this.devService);
+ return CommandResult.FAILURE;
+ }
+ }
+}
diff --git a/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/DevServicesProcessor.java b/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/DevServicesProcessor.java
new file mode 100644
index 0000000000000..caf05ad26ef7e
--- /dev/null
+++ b/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/DevServicesProcessor.java
@@ -0,0 +1,218 @@
+package io.quarkus.devservices.deployment;
+
+import static io.quarkus.deployment.dev.testing.MessageFormat.BOLD;
+import static io.quarkus.deployment.dev.testing.MessageFormat.GREEN;
+import static io.quarkus.deployment.dev.testing.MessageFormat.NO_BOLD;
+import static io.quarkus.deployment.dev.testing.MessageFormat.NO_UNDERLINE;
+import static io.quarkus.deployment.dev.testing.MessageFormat.RED;
+import static io.quarkus.deployment.dev.testing.MessageFormat.RESET;
+import static io.quarkus.deployment.dev.testing.MessageFormat.UNDERLINE;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.testcontainers.DockerClientFactory;
+
+import com.github.dockerjava.api.model.Container;
+import com.github.dockerjava.api.model.ContainerNetwork;
+import com.github.dockerjava.api.model.ContainerNetworkSettings;
+
+import io.quarkus.deployment.IsDevelopment;
+import io.quarkus.deployment.IsDockerWorking;
+import io.quarkus.deployment.annotations.BuildProducer;
+import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.builditem.ConsoleCommandBuildItem;
+import io.quarkus.deployment.builditem.DevServicesLauncherConfigResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem;
+import io.quarkus.deployment.builditem.LaunchModeBuildItem;
+import io.quarkus.deployment.console.ConsoleCommand;
+import io.quarkus.deployment.console.ConsoleStateManager;
+import io.quarkus.deployment.dev.devservices.ContainerInfo;
+import io.quarkus.deployment.dev.devservices.DevServiceDescriptionBuildItem;
+import io.quarkus.dev.spi.DevModeType;
+
+public class DevServicesProcessor {
+
+ private static final String EXEC_FORMAT = "docker exec -it %s /bin/bash";
+
+ private final IsDockerWorking isDockerWorking = new IsDockerWorking(true);
+
+ static volatile ConsoleStateManager.ConsoleContext context;
+ static volatile boolean logForwardEnabled = false;
+ static Map containerLogForwarders = new HashMap<>();
+
+ @BuildStep(onlyIf = { IsDevelopment.class })
+ public List config(
+ BuildProducer commandBuildItemBuildProducer,
+ LaunchModeBuildItem launchModeBuildItem,
+ Optional devServicesLauncherConfig,
+ List devServicesResults) {
+ List serviceDescriptions = buildServiceDescriptions(devServicesResults,
+ devServicesLauncherConfig);
+
+ for (DevServiceDescriptionBuildItem devService : serviceDescriptions) {
+ if (devService.hasContainerInfo()) {
+ containerLogForwarders.compute(devService.getContainerInfo().getId(),
+ (id, forwarder) -> Objects.requireNonNullElseGet(forwarder,
+ () -> new ContainerLogForwarder(devService)));
+ }
+ }
+
+ // Build commands if we are in local dev mode
+ if (launchModeBuildItem.getDevModeType().orElse(null) != DevModeType.LOCAL) {
+ return serviceDescriptions;
+ }
+
+ commandBuildItemBuildProducer.produce(
+ new ConsoleCommandBuildItem(new DevServicesCommand(serviceDescriptions)));
+
+ if (context == null) {
+ context = ConsoleStateManager.INSTANCE.createContext("Dev Services");
+ }
+ context.reset(
+ new ConsoleCommand('c', "Show dev services containers", null, () -> {
+ List descriptions = buildServiceDescriptions(devServicesResults,
+ devServicesLauncherConfig);
+ StringBuilder builder = new StringBuilder();
+ builder.append("\n\n")
+ .append(RED + "==" + RESET + " " + UNDERLINE + "Dev Services" + NO_UNDERLINE)
+ .append("\n\n");
+ for (DevServiceDescriptionBuildItem devService : descriptions) {
+ printDevService(builder, devService, true);
+ builder.append("\n");
+ }
+ System.out.println(builder);
+ }),
+ new ConsoleCommand('g', "Follow dev services logs to the console",
+ new ConsoleCommand.HelpState(() -> logForwardEnabled ? GREEN : RED,
+ () -> logForwardEnabled ? "enabled" : "disabled"),
+ this::toggleLogForwarders));
+ return serviceDescriptions;
+ }
+
+ private List buildServiceDescriptions(List devServicesResults,
+ Optional devServicesLauncherConfig) {
+ // Fetch container infos
+ Set containerIds = devServicesResults.stream()
+ .map(DevServicesResultBuildItem::getContainerId)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet());
+ Map containerInfos = fetchContainerInfos(containerIds);
+ // Build descriptions
+ Set configKeysFromDevServices = new HashSet<>();
+ List descriptions = new ArrayList<>();
+ for (DevServicesResultBuildItem buildItem : devServicesResults) {
+ configKeysFromDevServices.addAll(buildItem.getConfig().keySet());
+ descriptions.add(toDevServiceDescription(buildItem, containerInfos.get(buildItem.getContainerId())));
+ }
+ // Sort descriptions by name
+ descriptions.sort(Comparator.comparing(DevServiceDescriptionBuildItem::getName));
+ // Add description from other dev service configs as last
+ if (devServicesLauncherConfig.isPresent()) {
+ Map config = new HashMap<>(devServicesLauncherConfig.get().getConfig());
+ for (String key : configKeysFromDevServices) {
+ config.remove(key);
+ }
+ if (!config.isEmpty()) {
+ descriptions.add(new DevServiceDescriptionBuildItem("Other Dev Services", null, config));
+ }
+ }
+ return descriptions;
+ }
+
+ private Map fetchContainerInfos(Set containerIds) {
+ if (containerIds.isEmpty() || !isDockerWorking.getAsBoolean()) {
+ return Collections.emptyMap();
+ }
+ return DockerClientFactory.lazyClient().listContainersCmd()
+ .withIdFilter(containerIds)
+ .withShowAll(true)
+ .exec()
+ .stream()
+ .collect(Collectors.toMap(Container::getId, Function.identity()));
+ }
+
+ private DevServiceDescriptionBuildItem toDevServiceDescription(DevServicesResultBuildItem buildItem, Container container) {
+ if (container == null) {
+ return new DevServiceDescriptionBuildItem(buildItem.getName(), null, buildItem.getConfig());
+ } else {
+ return new DevServiceDescriptionBuildItem(buildItem.getName(), toContainerInfo(container), buildItem.getConfig());
+ }
+ }
+
+ private ContainerInfo toContainerInfo(Container container) {
+ return new ContainerInfo(container.getId(), container.getNames(), container.getImage(),
+ container.getStatus(), getNetworks(container), container.getLabels(), getExposedPorts(container));
+ }
+
+ private static String[] getNetworks(Container container) {
+ ContainerNetworkSettings networkSettings = container.getNetworkSettings();
+ if (networkSettings == null) {
+ return null;
+ }
+ Map networks = networkSettings.getNetworks();
+ if (networks == null) {
+ return null;
+ }
+ return networks.entrySet().stream()
+ .map(e -> {
+ List aliases = e.getValue().getAliases();
+ if (aliases == null) {
+ return e.getKey();
+ }
+ return e.getKey() + " (" + String.join(",", aliases) + ")";
+ })
+ .toArray(String[]::new);
+ }
+
+ private ContainerInfo.ContainerPort[] getExposedPorts(Container container) {
+ return Arrays.stream(container.getPorts())
+ .map(c -> new ContainerInfo.ContainerPort(c.getIp(), c.getPrivatePort(), c.getPublicPort(), c.getType()))
+ .toArray(ContainerInfo.ContainerPort[]::new);
+ }
+
+ private synchronized void toggleLogForwarders() {
+ if (logForwardEnabled) {
+ for (ContainerLogForwarder logForwarder : containerLogForwarders.values()) {
+ if (logForwarder.isRunning()) {
+ logForwarder.close();
+ }
+ }
+ logForwardEnabled = false;
+ } else {
+ for (ContainerLogForwarder logForwarder : containerLogForwarders.values()) {
+ logForwarder.start();
+ }
+ logForwardEnabled = true;
+ }
+ }
+
+ public static void printDevService(StringBuilder builder, DevServiceDescriptionBuildItem devService, boolean withStatus) {
+ if (devService.hasContainerInfo()) {
+ builder.append(BOLD).append(devService.getName()).append(NO_BOLD);
+ if (withStatus) {
+ builder.append(" - ").append(devService.getContainerInfo().getStatus());
+ }
+ builder.append("\n");
+ builder.append(String.format(" %-18s", "Container: "))
+ .append(devService.getContainerInfo().getId(), 0, 12)
+ .append(devService.getContainerInfo().formatNames())
+ .append(" ")
+ .append(devService.getContainerInfo().getImageName())
+ .append("\n");
+ builder.append(String.format(" %-18s", "Network: "))
+ .append(devService.getContainerInfo().formatNetworks())
+ .append(" - ")
+ .append(devService.getContainerInfo().formatPorts())
+ .append("\n");
+ builder.append(String.format(" %-18s", "Exec command: "))
+ .append(String.format(EXEC_FORMAT, devService.getContainerInfo().getShortId()))
+ .append("\n");
+ }
+ builder.append(String.format(" %-18s", "Injected Config: "))
+ .append(devService.formatConfigs())
+ .append("\n");
+ }
+
+}
diff --git a/extensions/devservices/derby/src/main/java/io/quarkus/devservices/derby/deployment/DerbyDevServicesProcessor.java b/extensions/devservices/derby/src/main/java/io/quarkus/devservices/derby/deployment/DerbyDevServicesProcessor.java
index f76d2805a0ac9..4a2389fe066cf 100644
--- a/extensions/devservices/derby/src/main/java/io/quarkus/devservices/derby/deployment/DerbyDevServicesProcessor.java
+++ b/extensions/devservices/derby/src/main/java/io/quarkus/devservices/derby/deployment/DerbyDevServicesProcessor.java
@@ -64,7 +64,7 @@ public RunningDevServicesDatasource startDatabase(Optional username, Opt
additionalArgs.append("=");
additionalArgs.append(i.getValue());
}
- return new RunningDevServicesDatasource(
+ return new RunningDevServicesDatasource(null,
"jdbc:derby://localhost:" + port + "/memory:" + datasourceName.orElse("quarkus") + ";create=true"
+ additionalArgs.toString(),
null,
diff --git a/extensions/devservices/h2/src/main/java/io/quarkus/devservices/h2/deployment/H2DevServicesProcessor.java b/extensions/devservices/h2/src/main/java/io/quarkus/devservices/h2/deployment/H2DevServicesProcessor.java
index a1b1be5614a94..1489eccd1af19 100644
--- a/extensions/devservices/h2/src/main/java/io/quarkus/devservices/h2/deployment/H2DevServicesProcessor.java
+++ b/extensions/devservices/h2/src/main/java/io/quarkus/devservices/h2/deployment/H2DevServicesProcessor.java
@@ -50,7 +50,7 @@ public RunningDevServicesDatasource startDatabase(Optional username, Opt
String connectionUrl = "jdbc:h2:tcp://localhost:" + tcpServer.getPort() + "/mem:"
+ datasourceName.orElse("default")
+ ";DB_CLOSE_DELAY=-1" + additionalArgs.toString();
- return new RunningDevServicesDatasource(
+ return new RunningDevServicesDatasource(null,
connectionUrl,
"sa",
"sa",
diff --git a/extensions/devservices/mariadb/src/main/java/io/quarkus/devservices/mariadb/deployment/MariaDBDevServicesProcessor.java b/extensions/devservices/mariadb/src/main/java/io/quarkus/devservices/mariadb/deployment/MariaDBDevServicesProcessor.java
index acbc20871749b..d0d6bc62f6428 100644
--- a/extensions/devservices/mariadb/src/main/java/io/quarkus/devservices/mariadb/deployment/MariaDBDevServicesProcessor.java
+++ b/extensions/devservices/mariadb/src/main/java/io/quarkus/devservices/mariadb/deployment/MariaDBDevServicesProcessor.java
@@ -52,7 +52,9 @@ public RunningDevServicesDatasource startDatabase(Optional username, Opt
LOG.info("Dev Services for MariaDB started.");
- return new RunningDevServicesDatasource(container.getEffectiveJdbcUrl(), container.getUsername(),
+ return new RunningDevServicesDatasource(container.getContainerId(),
+ container.getEffectiveJdbcUrl(),
+ container.getUsername(),
container.getPassword(),
new Closeable() {
@Override
diff --git a/extensions/devservices/mssql/src/main/java/io/quarkus/devservices/mssql/deployment/MSSQLDevServicesProcessor.java b/extensions/devservices/mssql/src/main/java/io/quarkus/devservices/mssql/deployment/MSSQLDevServicesProcessor.java
index 4b9d4fab083b9..412e0cf84c931 100644
--- a/extensions/devservices/mssql/src/main/java/io/quarkus/devservices/mssql/deployment/MSSQLDevServicesProcessor.java
+++ b/extensions/devservices/mssql/src/main/java/io/quarkus/devservices/mssql/deployment/MSSQLDevServicesProcessor.java
@@ -42,7 +42,9 @@ public RunningDevServicesDatasource startDatabase(Optional username, Opt
LOG.info("Dev Services for Microsoft SQL Server started.");
- return new RunningDevServicesDatasource(container.getEffectiveJdbcUrl(), container.getUsername(),
+ return new RunningDevServicesDatasource(container.getContainerId(),
+ container.getEffectiveJdbcUrl(),
+ container.getUsername(),
container.getPassword(),
new Closeable() {
@Override
diff --git a/extensions/devservices/mysql/src/main/java/io/quarkus/devservices/mysql/deployment/MySQLDevServicesProcessor.java b/extensions/devservices/mysql/src/main/java/io/quarkus/devservices/mysql/deployment/MySQLDevServicesProcessor.java
index 0947aacb927ed..2f6c834bff0eb 100644
--- a/extensions/devservices/mysql/src/main/java/io/quarkus/devservices/mysql/deployment/MySQLDevServicesProcessor.java
+++ b/extensions/devservices/mysql/src/main/java/io/quarkus/devservices/mysql/deployment/MySQLDevServicesProcessor.java
@@ -52,7 +52,9 @@ public RunningDevServicesDatasource startDatabase(Optional username, Opt
LOG.info("Dev Services for MySQL started.");
- return new RunningDevServicesDatasource(container.getEffectiveJdbcUrl(), container.getUsername(),
+ return new RunningDevServicesDatasource(container.getContainerId(),
+ container.getEffectiveJdbcUrl(),
+ container.getUsername(),
container.getPassword(),
new Closeable() {
@Override
diff --git a/extensions/devservices/oracle/src/main/java/io/quarkus/devservices/oracle/deployment/OracleDevServicesProcessor.java b/extensions/devservices/oracle/src/main/java/io/quarkus/devservices/oracle/deployment/OracleDevServicesProcessor.java
index 0f7b87ebb409f..c8372040eca3c 100644
--- a/extensions/devservices/oracle/src/main/java/io/quarkus/devservices/oracle/deployment/OracleDevServicesProcessor.java
+++ b/extensions/devservices/oracle/src/main/java/io/quarkus/devservices/oracle/deployment/OracleDevServicesProcessor.java
@@ -50,7 +50,9 @@ public RunningDevServicesDatasource startDatabase(Optional username, Opt
LOG.info("Dev Services for Oracle started.");
- return new RunningDevServicesDatasource(container.getEffectiveJdbcUrl(), container.getUsername(),
+ return new RunningDevServicesDatasource(container.getContainerId(),
+ container.getEffectiveJdbcUrl(),
+ container.getUsername(),
container.getPassword(),
new Closeable() {
@Override
diff --git a/extensions/devservices/pom.xml b/extensions/devservices/pom.xml
index 7dc1df2431f3a..84d2af8ab037e 100644
--- a/extensions/devservices/pom.xml
+++ b/extensions/devservices/pom.xml
@@ -27,6 +27,7 @@
db2
oracle
common
+ deployment
diff --git a/extensions/devservices/postgresql/src/main/java/io/quarkus/devservices/postgresql/deployment/PostgresqlDevServicesProcessor.java b/extensions/devservices/postgresql/src/main/java/io/quarkus/devservices/postgresql/deployment/PostgresqlDevServicesProcessor.java
index 32fb572e75bba..575e6612ba1d6 100644
--- a/extensions/devservices/postgresql/src/main/java/io/quarkus/devservices/postgresql/deployment/PostgresqlDevServicesProcessor.java
+++ b/extensions/devservices/postgresql/src/main/java/io/quarkus/devservices/postgresql/deployment/PostgresqlDevServicesProcessor.java
@@ -52,7 +52,9 @@ public RunningDevServicesDatasource startDatabase(Optional username, Opt
LOG.info("Dev Services for PostgreSQL started.");
- return new RunningDevServicesDatasource(container.getEffectiveJdbcUrl(), container.getUsername(),
+ return new RunningDevServicesDatasource(container.getContainerId(),
+ container.getEffectiveJdbcUrl(),
+ container.getUsername(),
container.getPassword(),
new Closeable() {
@Override
diff --git a/extensions/infinispan-client/deployment/pom.xml b/extensions/infinispan-client/deployment/pom.xml
index 35ec24fce9010..46f03935ec963 100644
--- a/extensions/infinispan-client/deployment/pom.xml
+++ b/extensions/infinispan-client/deployment/pom.xml
@@ -44,7 +44,7 @@
io.quarkus
- quarkus-devservices-common
+ quarkus-devservices-deployment
diff --git a/extensions/infinispan-client/deployment/src/main/java/io/quarkus/infinispan/client/deployment/devservices/InfinispanDevServiceProcessor.java b/extensions/infinispan-client/deployment/src/main/java/io/quarkus/infinispan/client/deployment/devservices/InfinispanDevServiceProcessor.java
index eecefb279b4cc..9a55fc0593ab6 100644
--- a/extensions/infinispan-client/deployment/src/main/java/io/quarkus/infinispan/client/deployment/devservices/InfinispanDevServiceProcessor.java
+++ b/extensions/infinispan-client/deployment/src/main/java/io/quarkus/infinispan/client/deployment/devservices/InfinispanDevServiceProcessor.java
@@ -1,25 +1,31 @@
package io.quarkus.infinispan.client.deployment.devservices;
import static io.quarkus.runtime.LaunchMode.DEVELOPMENT;
+import static org.infinispan.server.test.core.InfinispanContainer.DEFAULT_USERNAME;
import java.io.Closeable;
import java.time.Duration;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
import org.infinispan.client.hotrod.impl.ConfigurationProperties;
import org.infinispan.server.test.core.InfinispanContainer;
import org.jboss.logging.Logger;
+import org.jetbrains.annotations.NotNull;
+import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsDockerWorking;
import io.quarkus.deployment.IsNormal;
-import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.CuratedApplicationShutdownBuildItem;
-import io.quarkus.deployment.builditem.DevServicesConfigResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem.RunningDevService;
import io.quarkus.deployment.builditem.DevServicesSharedNetworkBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.console.ConsoleInstalledBuildItem;
@@ -47,75 +53,68 @@ public class InfinispanDevServiceProcessor {
private static final String DEFAULT_PASSWORD = "password";
private static final String QUARKUS = "quarkus.";
private static final String DOT = ".";
- private static volatile List closeables;
+ private static volatile List devServices;
private static volatile InfinispanClientDevServiceBuildTimeConfig.DevServiceConfiguration capturedDevServicesConfiguration;
private static volatile boolean first = true;
private static volatile Boolean dockerRunning = null;
@BuildStep(onlyIfNot = IsNormal.class, onlyIf = { GlobalDevServicesConfig.Enabled.class })
- public void startInfinispanContainers(LaunchModeBuildItem launchMode,
+ public List startInfinispanContainers(LaunchModeBuildItem launchMode,
List devServicesSharedNetworkBuildItem,
- BuildProducer devConfigProducer, InfinispanClientDevServiceBuildTimeConfig config,
+ InfinispanClientDevServiceBuildTimeConfig config,
Optional consoleInstalledBuildItem,
CuratedApplicationShutdownBuildItem closeBuildItem,
LoggingSetupBuildItem loggingSetupBuildItem, GlobalDevServicesConfig devServicesConfig) {
// figure out if we need to shut down and restart existing Infinispan containers
// if not and the Infinispan containers have already started we just return
- if (closeables != null) {
+ if (devServices != null) {
boolean restartRequired = !config.devService.equals(capturedDevServicesConfiguration);
if (!restartRequired) {
- return;
+ return devServices.stream().map(RunningDevService::toBuildItem).collect(Collectors.toList());
}
- for (Closeable closeable : closeables) {
+ for (Closeable closeable : devServices) {
try {
closeable.close();
} catch (Throwable e) {
log.error("Failed to stop infinispan container", e);
}
}
- closeables = null;
+ devServices = null;
capturedDevServicesConfiguration = null;
}
capturedDevServicesConfiguration = config.devService;
- List currentCloseables = new ArrayList<>();
+ List newDevServices = new ArrayList<>();
StartupLogCompressor compressor = new StartupLogCompressor(
(launchMode.isTest() ? "(test) " : "") + "Infinispan Dev Services Starting:", consoleInstalledBuildItem,
loggingSetupBuildItem);
try {
- StartResult startResult = startContainer(config.devService.devservices,
+ RunningDevService devService = startContainer(config.devService.devservices,
launchMode.getLaunchMode(),
!devServicesSharedNetworkBuildItem.isEmpty(), devServicesConfig.timeout);
- if (startResult == null) {
+ if (devService == null) {
compressor.close();
- return;
+ return null;
}
- currentCloseables.add(startResult.closeable);
-
- devConfigProducer
- .produce(new DevServicesConfigResultBuildItem(getConfigPrefix() + "server-list", startResult.serverList));
- devConfigProducer.produce(new DevServicesConfigResultBuildItem(getConfigPrefix() + "client-intelligence", "BASIC"));
- devConfigProducer
- .produce(new DevServicesConfigResultBuildItem(getConfigPrefix() + "auth-username", startResult.user));
- devConfigProducer
- .produce(new DevServicesConfigResultBuildItem(getConfigPrefix() + "auth-password", startResult.password));
- log.infof("The infinispan server is ready to accept connections on %s", startResult.serverList);
+ newDevServices.add(devService);
+ log.infof("The infinispan server is ready to accept connections on %s",
+ devService.getConfig().get(getConfigPrefix() + "server-list"));
compressor.close();
} catch (Throwable t) {
compressor.closeAndDumpCaptured();
throw new RuntimeException(t);
}
- closeables = currentCloseables;
+ devServices = newDevServices;
if (first) {
first = false;
Runnable closeTask = () -> {
dockerRunning = null;
- if (closeables != null) {
- for (Closeable closeable : closeables) {
+ if (devServices != null) {
+ for (Closeable closeable : devServices) {
try {
closeable.close();
} catch (Throwable t) {
@@ -124,14 +123,15 @@ public void startInfinispanContainers(LaunchModeBuildItem launchMode,
}
}
first = true;
- closeables = null;
+ devServices = null;
capturedDevServicesConfiguration = null;
};
closeBuildItem.addCloseTask(closeTask, true);
}
+ return devServices.stream().map(RunningDevService::toBuildItem).collect(Collectors.toList());
}
- private StartResult startContainer(
+ private RunningDevService startContainer(
InfinispanDevServicesConfig devServicesConfig, LaunchMode launchMode,
boolean useSharedNetwork, Optional timeout) {
if (!devServicesConfig.enabled) {
@@ -157,43 +157,39 @@ private StartResult startContainer(
return null;
}
- Supplier defaultInfinispanServerSupplier = () -> {
+ Supplier defaultInfinispanServerSupplier = () -> {
QuarkusInfinispanContainer infinispanContainer = new QuarkusInfinispanContainer(devServicesConfig.port,
launchMode == DEVELOPMENT ? devServicesConfig.serviceName : null, useSharedNetwork,
devServicesConfig.artifacts);
timeout.ifPresent(infinispanContainer::withStartupTimeout);
infinispanContainer.start();
- String serverList = infinispanContainer.getHost() + ":" + infinispanContainer.getPort();
- String user = infinispanContainer.getUser();
- String secret = infinispanContainer.getPassword();
- return new StartResult(serverList, user, secret, () -> infinispanContainer.close());
+
+ return getRunningDevService(infinispanContainer.getContainerId(), infinispanContainer::close,
+ infinispanContainer.getHost() + ":" + infinispanContainer.getPort(),
+ infinispanContainer.getUser(), infinispanContainer.getPassword());
};
return infinispanContainerLocator.locateContainer(devServicesConfig.serviceName, devServicesConfig.shared, launchMode)
- .map(containerAddress -> new StartResult(containerAddress.getUrl(),
- InfinispanContainer.DEFAULT_USERNAME, DEFAULT_PASSWORD, null))
+ .map(containerAddress -> getRunningDevService(containerAddress.getId(), null,
+ containerAddress.getUrl(), DEFAULT_USERNAME, DEFAULT_PASSWORD)) // TODO can this be always right ?
.orElseGet(defaultInfinispanServerSupplier);
+ }
+ @NotNull
+ private RunningDevService getRunningDevService(String containerId, Closeable closeable, String serverList,
+ String username, String password) {
+ Map config = new HashMap<>();
+ config.put(getConfigPrefix() + "server-list", serverList);
+ config.put(getConfigPrefix() + "client-intelligence", "BASIC");
+ config.put(getConfigPrefix() + "auth-username", username);
+ config.put(getConfigPrefix() + "auth-password", password);
+ return new RunningDevService(Feature.INFINISPAN_CLIENT.getName(), containerId, closeable, config);
}
private String getConfigPrefix() {
return QUARKUS + "infinispan-client" + DOT;
}
- private static class StartResult {
- private final String serverList;
- private final String user;
- private final String password;
- private final Closeable closeable;
-
- public StartResult(String sl, String user, String password, Closeable closeable) {
- this.serverList = sl;
- this.user = user;
- this.password = password;
- this.closeable = closeable;
- }
- }
-
private static class QuarkusInfinispanContainer extends InfinispanContainer {
private final OptionalInt fixedExposedPort;
private final boolean useSharedNetwork;
@@ -241,7 +237,7 @@ public int getPort() {
}
public String getUser() {
- return InfinispanContainer.DEFAULT_USERNAME;
+ return DEFAULT_USERNAME;
}
public String getPassword() {
diff --git a/extensions/kafka-client/deployment/pom.xml b/extensions/kafka-client/deployment/pom.xml
index fd25e333212f2..c1312bd48e115 100644
--- a/extensions/kafka-client/deployment/pom.xml
+++ b/extensions/kafka-client/deployment/pom.xml
@@ -64,7 +64,7 @@
io.quarkus
- quarkus-devservices-common
+ quarkus-devservices-deployment
diff --git a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/DevServicesKafkaBrokerBuildItem.java b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/DevServicesKafkaBrokerBuildItem.java
deleted file mode 100644
index 9889c7c577e10..0000000000000
--- a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/DevServicesKafkaBrokerBuildItem.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package io.quarkus.kafka.client.deployment;
-
-import io.quarkus.builder.item.SimpleBuildItem;
-
-public final class DevServicesKafkaBrokerBuildItem extends SimpleBuildItem {
-
- final String bootstrapServers;
-
- public DevServicesKafkaBrokerBuildItem(String bs) {
- this.bootstrapServers = bs;
- }
-
- public String getBootstrapServers() {
- return bootstrapServers;
- }
-
-}
diff --git a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/DevServicesKafkaProcessor.java b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/DevServicesKafkaProcessor.java
index 062f4e46fc533..2ce80507699e9 100644
--- a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/DevServicesKafkaProcessor.java
+++ b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/DevServicesKafkaProcessor.java
@@ -1,6 +1,5 @@
package io.quarkus.kafka.client.deployment;
-import java.io.Closeable;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
@@ -26,12 +25,13 @@
import org.jboss.logging.Logger;
import org.testcontainers.utility.DockerImageName;
+import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsDockerWorking;
import io.quarkus.deployment.IsNormal;
-import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.CuratedApplicationShutdownBuildItem;
-import io.quarkus.deployment.builditem.DevServicesConfigResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem.RunningDevService;
import io.quarkus.deployment.builditem.DevServicesSharedNetworkBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.console.ConsoleInstalledBuildItem;
@@ -65,82 +65,77 @@ public class DevServicesKafkaProcessor {
private static final ContainerLocator kafkaContainerLocator = new ContainerLocator(DEV_SERVICE_LABEL, KAFKA_PORT);
- static volatile Closeable closeable;
+ static volatile RunningDevService devService;
static volatile KafkaDevServiceCfg cfg;
static volatile boolean first = true;
private final IsDockerWorking isDockerWorking = new IsDockerWorking(true);
@BuildStep(onlyIfNot = IsNormal.class, onlyIf = GlobalDevServicesConfig.Enabled.class)
- public DevServicesKafkaBrokerBuildItem startKafkaDevService(
+ public DevServicesResultBuildItem startKafkaDevService(
LaunchModeBuildItem launchMode,
KafkaBuildTimeConfig kafkaClientBuildTimeConfig,
List devServicesSharedNetworkBuildItem,
- BuildProducer devServicePropertiesProducer,
Optional consoleInstalledBuildItem,
CuratedApplicationShutdownBuildItem closeBuildItem,
LoggingSetupBuildItem loggingSetupBuildItem, GlobalDevServicesConfig devServicesConfig) {
KafkaDevServiceCfg configuration = getConfiguration(kafkaClientBuildTimeConfig);
- if (closeable != null) {
+ if (devService != null) {
boolean shouldShutdownTheBroker = !configuration.equals(cfg);
if (!shouldShutdownTheBroker) {
- return null;
+ return devService.toBuildItem();
}
shutdownBroker();
cfg = null;
}
- KafkaBroker kafkaBroker;
- DevServicesKafkaBrokerBuildItem bootstrapServers;
+
StartupLogCompressor compressor = new StartupLogCompressor(
(launchMode.isTest() ? "(test) " : "") + "Kafka Dev Services Starting:",
consoleInstalledBuildItem, loggingSetupBuildItem);
try {
-
- kafkaBroker = startKafka(configuration, launchMode,
+ devService = startKafka(configuration, launchMode,
!devServicesSharedNetworkBuildItem.isEmpty(),
devServicesConfig.timeout);
- bootstrapServers = null;
- if (kafkaBroker != null) {
- closeable = kafkaBroker.getCloseable();
- devServicePropertiesProducer.produce(new DevServicesConfigResultBuildItem(
- KAFKA_BOOTSTRAP_SERVERS, kafkaBroker.getBootstrapServers()));
- bootstrapServers = new DevServicesKafkaBrokerBuildItem(kafkaBroker.getBootstrapServers());
- }
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 (closeable != null) {
+ if (devService != null) {
shutdownBroker();
}
first = true;
- closeable = null;
+ devService = null;
cfg = null;
};
closeBuildItem.addCloseTask(closeTask, true);
}
cfg = configuration;
- if (bootstrapServers != null) {
- if (kafkaBroker.isOwner()) {
- log.infof(
- "Dev Services for Kafka started. Other Quarkus applications in dev mode will find the "
- + "broker automatically. For Quarkus applications in production mode, you can connect to"
- + " this by starting your application with -Dkafka.bootstrap.servers=%s",
- bootstrapServers.getBootstrapServers());
- }
- createTopicPartitions(bootstrapServers.getBootstrapServers(), configuration);
+ if (devService.isOwner()) {
+ log.infof(
+ "Dev Services for Kafka started. Other Quarkus applications in dev mode will find the "
+ + "broker automatically. For Quarkus applications in production mode, you can connect to"
+ + " this by starting your application with -Dkafka.bootstrap.servers=%s",
+ getKafkaBootstrapServers());
}
+ createTopicPartitions(getKafkaBootstrapServers(), configuration);
+ return devService.toBuildItem();
+ }
- return bootstrapServers;
+ public static String getKafkaBootstrapServers() {
+ return devService.getConfig().get(KAFKA_BOOTSTRAP_SERVERS);
}
public void createTopicPartitions(String bootstrapServers, KafkaDevServiceCfg configuration) {
@@ -186,18 +181,18 @@ public void createTopicPartitions(String bootstrapServers, KafkaDevServiceCfg co
}
private void shutdownBroker() {
- if (closeable != null) {
+ if (devService != null) {
try {
- closeable.close();
+ devService.close();
} catch (Throwable e) {
log.error("Failed to stop the Kafka broker", e);
} finally {
- closeable = null;
+ devService = null;
}
}
}
- private KafkaBroker startKafka(KafkaDevServiceCfg config,
+ private RunningDevService startKafka(KafkaDevServiceCfg config,
LaunchModeBuildItem launchMode, boolean useSharedNetwork, Optional timeout) {
if (!config.devServicesEnabled) {
// explicitly disabled
@@ -228,7 +223,7 @@ private KafkaBroker startKafka(KafkaDevServiceCfg config,
launchMode.getLaunchMode());
// Starting the broker
- final Supplier defaultKafkaBrokerSupplier = () -> {
+ final Supplier defaultKafkaBrokerSupplier = () -> {
if (config.imageName.contains("strimzi")) {
StrimziKafkaContainer container = new StrimziKafkaContainer(config.imageName)
.withBrokerId(1)
@@ -244,9 +239,10 @@ private KafkaBroker startKafka(KafkaDevServiceCfg config,
timeout.ifPresent(container::withStartupTimeout);
container.start();
- return new KafkaBroker(
- container.getBootstrapServers(),
- container::close);
+ return new RunningDevService(Feature.KAFKA_CLIENT.getName(),
+ container.getContainerId(),
+ container::close,
+ KAFKA_BOOTSTRAP_SERVERS, container.getBootstrapServers());
} else {
RedPandaKafkaContainer container = new RedPandaKafkaContainer(
DockerImageName.parse(config.imageName),
@@ -256,13 +252,18 @@ private KafkaBroker startKafka(KafkaDevServiceCfg config,
timeout.ifPresent(container::withStartupTimeout);
container.start();
- return new KafkaBroker(
- container.getBootstrapServers(),
- container::close);
+ return new RunningDevService(Feature.KAFKA_CLIENT.getName(),
+ container.getContainerId(),
+ container::close,
+ KAFKA_BOOTSTRAP_SERVERS, container.getBootstrapServers());
}
};
- return maybeContainerAddress.map(containerAddress -> new KafkaBroker(containerAddress.getUrl(), null))
+ return maybeContainerAddress
+ .map(containerAddress -> new RunningDevService(Feature.KAFKA_CLIENT.getName(),
+ containerAddress.getId(),
+ null,
+ KAFKA_BOOTSTRAP_SERVERS, containerAddress.getUrl()))
.orElseGet(defaultKafkaBrokerSupplier);
}
@@ -290,28 +291,6 @@ private KafkaDevServiceCfg getConfiguration(KafkaBuildTimeConfig cfg) {
return new KafkaDevServiceCfg(devServicesConfig);
}
- private static class KafkaBroker {
- private final String url;
- private final Closeable closeable;
-
- public KafkaBroker(String url, Closeable closeable) {
- this.url = url;
- this.closeable = closeable;
- }
-
- public boolean isOwner() {
- return closeable != null;
- }
-
- public String getBootstrapServers() {
- return url;
- }
-
- public Closeable getCloseable() {
- return closeable;
- }
- }
-
private static final class KafkaDevServiceCfg {
private final boolean devServicesEnabled;
private final String imageName;
diff --git a/extensions/mongodb-client/deployment/pom.xml b/extensions/mongodb-client/deployment/pom.xml
index 4754d27575919..d360655d4af4b 100644
--- a/extensions/mongodb-client/deployment/pom.xml
+++ b/extensions/mongodb-client/deployment/pom.xml
@@ -50,7 +50,7 @@
io.quarkus
- quarkus-devservices-common
+ quarkus-devservices-deployment
io.quarkus
diff --git a/extensions/mongodb-client/deployment/src/main/java/io/quarkus/mongodb/deployment/DevServicesMongoProcessor.java b/extensions/mongodb-client/deployment/src/main/java/io/quarkus/mongodb/deployment/DevServicesMongoProcessor.java
index d7b2ab60da959..48b86d6db3d5c 100644
--- a/extensions/mongodb-client/deployment/src/main/java/io/quarkus/mongodb/deployment/DevServicesMongoProcessor.java
+++ b/extensions/mongodb-client/deployment/src/main/java/io/quarkus/mongodb/deployment/DevServicesMongoProcessor.java
@@ -21,12 +21,13 @@
import com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.message.BasicNameValuePair;
import com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.net.URLEncodedUtils;
+import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsDockerWorking;
import io.quarkus.deployment.IsNormal;
-import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.CuratedApplicationShutdownBuildItem;
-import io.quarkus.deployment.builditem.DevServicesConfigResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem.RunningDevService;
import io.quarkus.deployment.builditem.DevServicesSharedNetworkBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.console.ConsoleInstalledBuildItem;
@@ -41,17 +42,16 @@ public class DevServicesMongoProcessor {
private static final Logger log = Logger.getLogger(DevServicesMongoProcessor.class);
- static volatile List closeables;
+ static volatile List devServices;
static volatile Map capturedProperties;
static volatile boolean first = true;
private final IsDockerWorking isDockerWorking = new IsDockerWorking(true);
@BuildStep(onlyIfNot = IsNormal.class, onlyIf = GlobalDevServicesConfig.Enabled.class)
- public void startMongo(List mongoConnections,
+ public List startMongo(List mongoConnections,
MongoClientBuildTimeConfig mongoClientBuildTimeConfig,
List devServicesSharedNetworkBuildItem,
- BuildProducer devServices,
Optional consoleInstalledBuildItem,
CuratedApplicationShutdownBuildItem closeBuildItem,
LaunchModeBuildItem launchMode,
@@ -65,10 +65,10 @@ public void startMongo(List mongoConnections,
// TODO: handle named connections as well
if (connectionNames.size() != 1) {
- return;
+ return null;
}
if (!isDefault(connectionNames.get(0))) {
- return;
+ return null;
}
Map currentCapturedProperties = captureProperties(connectionNames,
@@ -76,44 +76,40 @@ public void startMongo(List mongoConnections,
//figure out if we need to shut down and restart existing databases
//if not and the DB's have already started we just return
- if (closeables != null) {
+ if (devServices != null) {
boolean restartRequired = !currentCapturedProperties.equals(capturedProperties);
if (!restartRequired) {
- return;
+ return devServices.stream().map(RunningDevService::toBuildItem).collect(Collectors.toList());
}
- for (Closeable i : closeables) {
+ for (Closeable i : devServices) {
try {
i.close();
} catch (Throwable e) {
log.error("Failed to stop database", e);
}
}
- closeables = null;
+ devServices = null;
capturedProperties = null;
}
- List currentCloseables = new ArrayList<>(mongoConnections.size());
+ List newDevServices = new ArrayList<>(mongoConnections.size());
// TODO: we need to go through each connection
String connectionName = connectionNames.get(0);
- StartResult startResult;
+ RunningDevService devService;
StartupLogCompressor compressor = new StartupLogCompressor(
(launchMode.isTest() ? "(test) " : "") + "Mongo Dev Services Starting:", consoleInstalledBuildItem,
loggingSetupBuildItem);
try {
- startResult = startMongo(connectionName, currentCapturedProperties.get(connectionName),
+ devService = startMongo(connectionName, currentCapturedProperties.get(connectionName),
!devServicesSharedNetworkBuildItem.isEmpty(), globalDevServicesConfig.timeout);
compressor.close();
} catch (Throwable t) {
compressor.closeAndDumpCaptured();
throw new RuntimeException(t);
}
- if (startResult != null) {
- currentCloseables.add(startResult.getCloseable());
- String connectionStringPropertyName = getConfigPrefix(connectionName) + "connection-string";
- String connectionStringPropertyValue = startResult.getUrl();
- devServices.produce(
- new DevServicesConfigResultBuildItem(connectionStringPropertyName, connectionStringPropertyValue));
+ if (devService != null) {
+ newDevServices.add(devService);
}
if (first) {
@@ -121,8 +117,8 @@ public void startMongo(List mongoConnections,
Runnable closeTask = new Runnable() {
@Override
public void run() {
- if (closeables != null) {
- for (Closeable i : closeables) {
+ if (devServices != null) {
+ for (Closeable i : devServices) {
try {
i.close();
} catch (Throwable t) {
@@ -131,18 +127,18 @@ public void run() {
}
}
first = true;
- closeables = null;
+ devServices = null;
capturedProperties = null;
}
};
closeBuildItem.addCloseTask(closeTask, true);
}
- closeables = currentCloseables;
+ devServices = newDevServices;
capturedProperties = currentCapturedProperties;
-
+ return devServices.stream().map(RunningDevService::toBuildItem).collect(Collectors.toList());
}
- private StartResult startMongo(String connectionName, CapturedProperties capturedProperties, boolean useSharedNetwork,
+ private RunningDevService startMongo(String connectionName, CapturedProperties capturedProperties, boolean useSharedNetwork,
Optional timeout) {
if (!capturedProperties.devServicesEnabled) {
// explicitly disabled
@@ -187,14 +183,8 @@ private StartResult startMongo(String connectionName, CapturedProperties capture
.map(e -> new BasicNameValuePair(e.getKey(), e.getValue())).collect(Collectors.toList()),
StandardCharsets.UTF_8);
}
- return new StartResult(
- effectiveURL,
- new Closeable() {
- @Override
- public void close() {
- mongoDBContainer.close();
- }
- });
+ return new RunningDevService(Feature.MONGODB_CLIENT.getName(), mongoDBContainer.getContainerId(),
+ mongoDBContainer::close, getConfigPrefix(connectionName) + "connection-string", effectiveURL);
}
private String getConfigPrefix(String connectionName) {
@@ -226,24 +216,6 @@ private CapturedProperties captureProperties(String connectionName, MongoClientB
devServicesConfig.imageName.orElse(null), devServicesConfig.port.orElse(null), devServicesConfig.properties);
}
- private static class StartResult {
- private final String url;
- private final Closeable closeable;
-
- public StartResult(String url, Closeable closeable) {
- this.url = url;
- this.closeable = closeable;
- }
-
- public String getUrl() {
- return url;
- }
-
- public Closeable getCloseable() {
- return closeable;
- }
- }
-
private static final class CapturedProperties {
private final String database;
private final String connectionString;
diff --git a/extensions/oidc-client-filter/deployment/pom.xml b/extensions/oidc-client-filter/deployment/pom.xml
index 53288fad78699..798226c3af672 100644
--- a/extensions/oidc-client-filter/deployment/pom.xml
+++ b/extensions/oidc-client-filter/deployment/pom.xml
@@ -46,6 +46,12 @@
io.quarkus
quarkus-test-keycloak-server
test
+
+
+ junit
+ junit
+
+
io.quarkus
diff --git a/extensions/oidc-client/deployment/pom.xml b/extensions/oidc-client/deployment/pom.xml
index 1ff773f65cedb..2e44f29029b7c 100644
--- a/extensions/oidc-client/deployment/pom.xml
+++ b/extensions/oidc-client/deployment/pom.xml
@@ -34,6 +34,10 @@
io.quarkus
quarkus-oidc-common-deployment
+
+ io.quarkus
+ quarkus-devservices-deployment
+
io.quarkus
diff --git a/extensions/oidc/deployment/pom.xml b/extensions/oidc/deployment/pom.xml
index 12eb9289d154a..ff5393df036d1 100644
--- a/extensions/oidc/deployment/pom.xml
+++ b/extensions/oidc/deployment/pom.xml
@@ -37,6 +37,10 @@
io.quarkus
quarkus-security-deployment
+
+ io.quarkus
+ quarkus-devservices-deployment
+
io.quarkus
quarkus-jsonp-deployment
@@ -61,7 +65,7 @@
io.quarkus
- quarkus-devservices-common
+ quarkus-devservices-deployment
diff --git a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java
index 4c0b520e68761..03ba6fb0fb0ee 100644
--- a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java
+++ b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java
@@ -1,6 +1,5 @@
package io.quarkus.oidc.deployment.devservices.keycloak;
-import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
@@ -14,7 +13,6 @@
import java.nio.file.attribute.FileTime;
import java.time.Duration;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -39,12 +37,13 @@
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.DockerImageName;
+import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsDockerWorking;
import io.quarkus.deployment.IsNormal;
-import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.CuratedApplicationShutdownBuildItem;
-import io.quarkus.deployment.builditem.DevServicesConfigResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem.RunningDevService;
import io.quarkus.deployment.builditem.DevServicesSharedNetworkBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.console.ConsoleInstalledBuildItem;
@@ -66,7 +65,6 @@
import io.vertx.mutiny.ext.web.client.WebClient;
public class KeycloakDevServicesProcessor {
- static volatile DevServicesConfig capturedDevServicesConfiguration;
static volatile Vertx vertxInstance;
private static final Logger LOG = Logger.getLogger(KeycloakDevServicesProcessor.class);
@@ -115,18 +113,15 @@ public class KeycloakDevServicesProcessor {
private static final ContainerLocator keycloakDevModeContainerLocator = new ContainerLocator(DEV_SERVICE_LABEL,
KEYCLOAK_PORT);
- private static volatile List closeables;
+ private static volatile RunningDevService devService;
+ static volatile DevServicesConfig capturedDevServicesConfiguration;
private static volatile boolean first = true;
- private static volatile String capturedKeycloakInternalURL;
- private static volatile String capturedKeycloakHostURL;
private static volatile FileTime capturedRealmFileLastModifiedDate;
private final IsDockerWorking isDockerWorking = new IsDockerWorking(true);
- private static volatile KeycloakDevServicesConfigBuildItem existingDevServiceConfig;
@BuildStep(onlyIfNot = IsNormal.class, onlyIf = { IsEnabled.class, GlobalDevServicesConfig.Enabled.class })
- public KeycloakDevServicesConfigBuildItem startKeycloakContainer(
+ public DevServicesResultBuildItem startKeycloakContainer(
List devServicesSharedNetworkBuildItem,
- BuildProducer devServices,
Optional oidcProviderBuildItem,
KeycloakBuildTimeConfig config,
CuratedApplicationShutdownBuildItem closeBuildItem,
@@ -143,7 +138,7 @@ public KeycloakDevServicesConfigBuildItem startKeycloakContainer(
DevServicesConfig currentDevServicesConfiguration = config.devservices;
// Figure out if we need to shut down and restart any existing Keycloak container
// if not and the Keycloak container has already started we just return
- if (closeables != null) {
+ if (devService != null) {
boolean restartRequired = !currentDevServicesConfiguration.equals(capturedDevServicesConfiguration);
if (!restartRequired) {
FileTime currentRealmFileLastModifiedDate = getRealmFileLastModifiedDate(
@@ -155,46 +150,43 @@ public KeycloakDevServicesConfigBuildItem startKeycloakContainer(
}
}
if (!restartRequired) {
- return existingDevServiceConfig;
+ return devService.toBuildItem();
}
- for (Closeable closeable : closeables) {
- try {
- closeable.close();
- } catch (Throwable e) {
- LOG.error("Failed to stop Keycloak container", e);
- }
+ try {
+ devService.close();
+ } catch (Throwable e) {
+ LOG.error("Failed to stop Keycloak container", e);
}
- closeables = null;
+ devService = null;
capturedDevServicesConfiguration = null;
- capturedKeycloakInternalURL = null;
- existingDevServiceConfig = null;
}
capturedDevServicesConfiguration = currentDevServicesConfiguration;
- StartResult startResult;
StartupLogCompressor compressor = new StartupLogCompressor(
(launchMode.isTest() ? "(test) " : "") + "KeyCloak Dev Services Starting:",
consoleInstalledBuildItem, loggingSetupBuildItem);
+ if (vertxInstance == null) {
+ vertxInstance = Vertx.vertx();
+ }
try {
- startResult = startContainer(!devServicesSharedNetworkBuildItem.isEmpty(), devServicesConfig.timeout);
- if (startResult == null) {
+ RunningDevService newDevService = startContainer(!devServicesSharedNetworkBuildItem.isEmpty(),
+ devServicesConfig.timeout);
+ if (newDevService == null) {
compressor.close();
return null;
}
- closeables = startResult.closeable != null ? Collections.singletonList(startResult.closeable) : null;
+ devService = newDevService;
if (first) {
first = false;
Runnable closeTask = new Runnable() {
@Override
public void run() {
- if (closeables != null) {
- for (Closeable closeable : closeables) {
- try {
- closeable.close();
- } catch (Throwable t) {
- LOG.error("Failed to stop Keycloak container", t);
- }
+ if (devService != null) {
+ try {
+ devService.close();
+ } catch (Throwable t) {
+ LOG.error("Failed to stop Keycloak container", t);
}
}
if (vertxInstance != null) {
@@ -205,7 +197,7 @@ public void run() {
}
}
first = true;
- closeables = null;
+ devService = null;
capturedDevServicesConfiguration = null;
vertxInstance = null;
capturedRealmFileLastModifiedDate = null;
@@ -214,11 +206,6 @@ public void run() {
closeBuildItem.addCloseTask(closeTask, true);
}
- capturedKeycloakInternalURL = startResult.internalURL;
- capturedKeycloakHostURL = startResult.hostURL;
- if (vertxInstance == null) {
- vertxInstance = Vertx.vertx();
- }
capturedRealmFileLastModifiedDate = getRealmFileLastModifiedDate(capturedDevServicesConfiguration.realmPath);
compressor.close();
} catch (Throwable t) {
@@ -227,20 +214,19 @@ public void run() {
}
LOG.info("Dev Services for Keycloak started.");
- return prepareConfiguration(startResult.realmRep, startResult.keycloakX, devServices);
+ return devService.toBuildItem();
}
private String startURL(String host, Integer port, boolean isKeyCloakX) {
return "http://" + host + ":" + port + (isKeyCloakX ? "" : "/auth");
}
- private KeycloakDevServicesConfigBuildItem prepareConfiguration(RealmRepresentation realmRep, boolean keycloakX,
- BuildProducer devServices) {
+ private Map prepareConfiguration(String internalURL, String hostURL, RealmRepresentation realmRep,
+ boolean keycloakX) {
final String realmName = realmRep != null ? realmRep.getRealm() : getDefaultRealmName();
- final String authServerInternalUrl = realmsURL(capturedKeycloakInternalURL, realmName);
+ final String authServerInternalUrl = realmsURL(internalURL, realmName);
- String clientAuthServerBaseUrl = capturedKeycloakHostURL != null ? capturedKeycloakHostURL
- : capturedKeycloakInternalURL;
+ String clientAuthServerBaseUrl = hostURL != null ? hostURL : internalURL;
String clientAuthServerUrl = realmsURL(clientAuthServerBaseUrl, realmName);
String oidcClientId = getOidcClientId();
@@ -255,24 +241,15 @@ private KeycloakDevServicesConfigBuildItem prepareConfiguration(RealmRepresentat
} else if (realmRep != null && keycloakX) {
createRealm(clientAuthServerBaseUrl, realmRep);
}
- devServices.produce(new DevServicesConfigResultBuildItem(KEYCLOAK_URL_KEY, capturedKeycloakInternalURL));
- devServices.produce(new DevServicesConfigResultBuildItem(AUTH_SERVER_URL_CONFIG_KEY, authServerInternalUrl));
- devServices.produce(new DevServicesConfigResultBuildItem(CLIENT_AUTH_SERVER_URL_CONFIG_KEY, clientAuthServerUrl));
- devServices.produce(new DevServicesConfigResultBuildItem(APPLICATION_TYPE_CONFIG_KEY, oidcApplicationType));
- devServices.produce(new DevServicesConfigResultBuildItem(CLIENT_ID_CONFIG_KEY, oidcClientId));
- devServices.produce(new DevServicesConfigResultBuildItem(CLIENT_SECRET_CONFIG_KEY, oidcClientSecret));
-
- Map configProperties = new HashMap<>();
- configProperties.put(KEYCLOAK_URL_KEY, capturedKeycloakInternalURL);
- configProperties.put(KEYCLOAK_REALM_KEY, realmName);
+
+ Map configProperties = new HashMap<>();
+ configProperties.put(KEYCLOAK_URL_KEY, internalURL);
configProperties.put(AUTH_SERVER_URL_CONFIG_KEY, authServerInternalUrl);
+ configProperties.put(CLIENT_AUTH_SERVER_URL_CONFIG_KEY, clientAuthServerUrl);
configProperties.put(APPLICATION_TYPE_CONFIG_KEY, oidcApplicationType);
configProperties.put(CLIENT_ID_CONFIG_KEY, oidcClientId);
configProperties.put(CLIENT_SECRET_CONFIG_KEY, oidcClientSecret);
- configProperties.put(OIDC_USERS, users);
-
- existingDevServiceConfig = new KeycloakDevServicesConfigBuildItem(configProperties);
- return existingDevServiceConfig;
+ return configProperties;
}
private String realmsURL(String baseURL, String realmName) {
@@ -283,7 +260,7 @@ private String getDefaultRealmName() {
return capturedDevServicesConfiguration.realmName.orElse("quarkus");
}
- private StartResult startContainer(boolean useSharedNetwork, Optional timeout) {
+ private RunningDevService startContainer(boolean useSharedNetwork, Optional timeout) {
if (!capturedDevServicesConfiguration.enabled) {
// explicitly disabled
LOG.debug("Not starting Dev Services for Keycloak as it has been disabled in the config");
@@ -311,7 +288,7 @@ private StartResult startContainer(boolean useSharedNetwork, Optional
String imageName = capturedDevServicesConfiguration.imageName;
DockerImageName dockerImageName = DockerImageName.parse(imageName).asCompatibleSubstituteFor(imageName);
- final Supplier defaultKeycloakContainerSupplier = () -> {
+ final Supplier defaultKeycloakContainerSupplier = () -> {
QuarkusOidcContainer oidcContainer = new QuarkusOidcContainer(dockerImageName,
capturedDevServicesConfiguration.port,
@@ -324,28 +301,24 @@ private StartResult startContainer(boolean useSharedNetwork, Optional
timeout.ifPresent(oidcContainer::withStartupTimeout);
oidcContainer.start();
- return new StartResult(
- startURL(oidcContainer.getHost(), oidcContainer.getPort(), oidcContainer.keycloakX),
- oidcContainer.useSharedNetwork
- ? startURL("localhost", oidcContainer.fixedExposedPort.getAsInt(), oidcContainer.keycloakX)
- : null,
- oidcContainer.keycloakX,
- oidcContainer.realmRep,
- new Closeable() {
- @Override
- public void close() {
- oidcContainer.close();
-
- LOG.info("Dev Services for Keycloak shut down.");
- }
- });
+ String internalUrl = startURL(oidcContainer.getHost(), oidcContainer.getPort(), oidcContainer.keycloakX);
+ String hostUrl = oidcContainer.useSharedNetwork
+ ? startURL("localhost", oidcContainer.fixedExposedPort.getAsInt(), oidcContainer.keycloakX)
+ : null;
+ Map configs = prepareConfiguration(internalUrl, hostUrl, oidcContainer.realmRep,
+ oidcContainer.keycloakX);
+ return new RunningDevService(Feature.KEYCLOAK_AUTHORIZATION.getName(), oidcContainer.getContainerId(),
+ oidcContainer::close, configs);
};
return maybeContainerAddress
- .map(containerAddress -> new StartResult(
- getSharedContainerUrl(containerAddress),
- getSharedContainerUrl(containerAddress), // TODO: this probably needs to be addressed
- false, null, null))
+ .map(containerAddress -> {
+ // TODO: this probably needs to be addressed
+ Map configs = prepareConfiguration(getSharedContainerUrl(containerAddress),
+ getSharedContainerUrl(containerAddress), null, false);
+ return new RunningDevService(Feature.KEYCLOAK_AUTHORIZATION.getName(),
+ containerAddress.getId(), null, configs);
+ })
.orElseGet(defaultKeycloakContainerSupplier);
}
@@ -360,24 +333,7 @@ private String getSharedContainerUrl(ContainerAddress containerAddress) {
+ ":" + containerAddress.getPort();
}
- private static class StartResult {
- private final String internalURL;
- private final String hostURL;
- private final RealmRepresentation realmRep;
- private final Closeable closeable;
- private final boolean keycloakX;
-
- public StartResult(String internalURL, String hostURL,
- boolean keycloakX, RealmRepresentation realmRep, Closeable closeable) {
- this.internalURL = internalURL;
- this.hostURL = hostURL;
- this.keycloakX = keycloakX;
- this.realmRep = realmRep;
- this.closeable = closeable;
- }
- }
-
- private static class QuarkusOidcContainer extends GenericContainer {
+ private static class QuarkusOidcContainer extends GenericContainer {
private final OptionalInt fixedExposedPort;
private final boolean useSharedNetwork;
private final Optional realmPath;
diff --git a/extensions/panache/hibernate-orm-panache-kotlin/deployment/pom.xml b/extensions/panache/hibernate-orm-panache-kotlin/deployment/pom.xml
index 2cf35be7c5612..4c859dfcd8043 100644
--- a/extensions/panache/hibernate-orm-panache-kotlin/deployment/pom.xml
+++ b/extensions/panache/hibernate-orm-panache-kotlin/deployment/pom.xml
@@ -15,6 +15,12 @@
io.quarkus
quarkus-hibernate-orm-panache-common-deployment
+
+
+ org.jetbrains
+ annotations
+
+
io.quarkus
diff --git a/extensions/redis-client/deployment/pom.xml b/extensions/redis-client/deployment/pom.xml
index 87c4b51eb38b6..38fc20b97c3a2 100644
--- a/extensions/redis-client/deployment/pom.xml
+++ b/extensions/redis-client/deployment/pom.xml
@@ -21,7 +21,7 @@
io.quarkus
- quarkus-devservices-common
+ quarkus-devservices-deployment
io.quarkus
diff --git a/extensions/redis-client/deployment/src/main/java/io/quarkus/redis/client/deployment/DevServicesRedisProcessor.java b/extensions/redis-client/deployment/src/main/java/io/quarkus/redis/client/deployment/DevServicesRedisProcessor.java
index ba385ebf978ad..5933c4451642c 100644
--- a/extensions/redis-client/deployment/src/main/java/io/quarkus/redis/client/deployment/DevServicesRedisProcessor.java
+++ b/extensions/redis-client/deployment/src/main/java/io/quarkus/redis/client/deployment/DevServicesRedisProcessor.java
@@ -13,17 +13,19 @@
import java.util.Optional;
import java.util.OptionalInt;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
import org.jboss.logging.Logger;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.DockerImageName;
+import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsDockerWorking.IsDockerRunningSilent;
import io.quarkus.deployment.IsNormal;
-import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.CuratedApplicationShutdownBuildItem;
-import io.quarkus.deployment.builditem.DevServicesConfigResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem.RunningDevService;
import io.quarkus.deployment.builditem.DevServicesSharedNetworkBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.console.ConsoleInstalledBuildItem;
@@ -54,15 +56,15 @@ public class DevServicesRedisProcessor {
private static final String QUARKUS = "quarkus.";
private static final String DOT = ".";
- private static volatile List closeables;
+ private static volatile List devServices;
private static volatile Map capturedDevServicesConfiguration;
private static volatile boolean first = true;
private static volatile Boolean dockerRunning = null;
@BuildStep(onlyIfNot = IsNormal.class, onlyIf = { GlobalDevServicesConfig.Enabled.class })
- public void startRedisContainers(LaunchModeBuildItem launchMode,
+ public List startRedisContainers(LaunchModeBuildItem launchMode,
List devServicesSharedNetworkBuildItem,
- BuildProducer devConfigProducer, RedisBuildTimeConfig config,
+ RedisBuildTimeConfig config,
Optional consoleInstalledBuildItem,
CuratedApplicationShutdownBuildItem closeBuildItem,
LoggingSetupBuildItem loggingSetupBuildItem,
@@ -73,24 +75,24 @@ public void startRedisContainers(LaunchModeBuildItem launchMode,
// figure out if we need to shut down and restart existing redis containers
// if not and the redis containers have already started we just return
- if (closeables != null) {
+ if (devServices != null) {
boolean restartRequired = !currentDevServicesConfiguration.equals(capturedDevServicesConfiguration);
if (!restartRequired) {
- return;
+ return devServices.stream().map(RunningDevService::toBuildItem).collect(Collectors.toList());
}
- for (Closeable closeable : closeables) {
+ for (Closeable closeable : devServices) {
try {
closeable.close();
} catch (Throwable e) {
log.error("Failed to stop redis container", e);
}
}
- closeables = null;
+ devServices = null;
capturedDevServicesConfiguration = null;
}
capturedDevServicesConfiguration = currentDevServicesConfiguration;
- List currentCloseables = new ArrayList<>();
+ List newDevServices = new ArrayList<>();
StartupLogCompressor compressor = new StartupLogCompressor(
(launchMode.isTest() ? "(test) " : "") + "Redis Dev Services Starting:", consoleInstalledBuildItem,
@@ -98,16 +100,16 @@ public void startRedisContainers(LaunchModeBuildItem launchMode,
try {
for (Entry entry : currentDevServicesConfiguration.entrySet()) {
String connectionName = entry.getKey();
- StartResult startResult = startContainer(connectionName, entry.getValue().devservices,
+ RunningDevService devService = startContainer(connectionName, entry.getValue().devservices,
launchMode.getLaunchMode(),
!devServicesSharedNetworkBuildItem.isEmpty(), devServicesConfig.timeout);
- if (startResult == null) {
+ if (devService == null) {
continue;
}
- currentCloseables.add(startResult.closeable);
+ newDevServices.add(devService);
String configKey = getConfigPrefix(connectionName) + RedisConfig.HOSTS_CONFIG_NAME;
- devConfigProducer.produce(new DevServicesConfigResultBuildItem(configKey, startResult.url));
- log.infof("The %s redis server is ready to accept connections on %s", connectionName, startResult.url);
+ log.infof("The %s redis server is ready to accept connections on %s", connectionName,
+ devService.getConfig().get(configKey));
}
compressor.close();
} catch (Throwable t) {
@@ -115,14 +117,14 @@ public void startRedisContainers(LaunchModeBuildItem launchMode,
throw new RuntimeException(t);
}
- closeables = currentCloseables;
+ devServices = newDevServices;
if (first) {
first = false;
Runnable closeTask = () -> {
dockerRunning = null;
- if (closeables != null) {
- for (Closeable closeable : closeables) {
+ if (devServices != null) {
+ for (Closeable closeable : devServices) {
try {
closeable.close();
} catch (Throwable t) {
@@ -131,14 +133,15 @@ public void startRedisContainers(LaunchModeBuildItem launchMode,
}
}
first = true;
- closeables = null;
+ devServices = null;
capturedDevServicesConfiguration = null;
};
closeBuildItem.addCloseTask(closeTask, true);
}
+ return devServices.stream().map(RunningDevService::toBuildItem).collect(Collectors.toList());
}
- private StartResult startContainer(String connectionName, DevServicesConfig devServicesConfig, LaunchMode launchMode,
+ private RunningDevService startContainer(String connectionName, DevServicesConfig devServicesConfig, LaunchMode launchMode,
boolean useSharedNetwork, Optional timeout) {
if (!devServicesConfig.enabled) {
// explicitly disabled
@@ -170,20 +173,20 @@ private StartResult startContainer(String connectionName, DevServicesConfig devS
DockerImageName dockerImageName = DockerImageName.parse(devServicesConfig.imageName.orElse(REDIS_6_ALPINE))
.asCompatibleSubstituteFor(REDIS_6_ALPINE);
- Supplier defaultRedisServerSupplier = () -> {
+ Supplier defaultRedisServerSupplier = () -> {
QuarkusPortRedisContainer redisContainer = new QuarkusPortRedisContainer(dockerImageName, devServicesConfig.port,
launchMode == DEVELOPMENT ? devServicesConfig.serviceName : null, useSharedNetwork);
timeout.ifPresent(redisContainer::withStartupTimeout);
redisContainer.start();
String redisHost = REDIS_SCHEME + redisContainer.getHost() + ":" + redisContainer.getPort();
- return new StartResult(redisHost,
- redisContainer::close);
+ return new RunningDevService(Feature.REDIS_CLIENT.getName(), redisContainer.getContainerId(),
+ redisContainer::close, configPrefix + RedisConfig.HOSTS_CONFIG_NAME, redisHost);
};
return redisContainerLocator.locateContainer(devServicesConfig.serviceName, devServicesConfig.shared, launchMode)
- .map(containerAddress -> new StartResult(containerAddress.getUrl(), null))
+ .map(containerAddress -> new RunningDevService(Feature.REDIS_CLIENT.getName(), containerAddress.getId(),
+ null, configPrefix + RedisConfig.HOSTS_CONFIG_NAME, containerAddress.getUrl()))
.orElseGet(defaultRedisServerSupplier);
-
}
private String getConfigPrefix(String connectionName) {
@@ -194,16 +197,6 @@ private String getConfigPrefix(String connectionName) {
return configPrefix;
}
- private static class StartResult {
- private final String url;
- private final Closeable closeable;
-
- public StartResult(String url, Closeable closeable) {
- this.url = url;
- this.closeable = closeable;
- }
- }
-
private static class QuarkusPortRedisContainer extends GenericContainer {
private final OptionalInt fixedExposedPort;
private final boolean useSharedNetwork;
diff --git a/extensions/smallrye-reactive-messaging-amqp/deployment/pom.xml b/extensions/smallrye-reactive-messaging-amqp/deployment/pom.xml
index 180508b2134ae..60bd5b514e3af 100644
--- a/extensions/smallrye-reactive-messaging-amqp/deployment/pom.xml
+++ b/extensions/smallrye-reactive-messaging-amqp/deployment/pom.xml
@@ -103,7 +103,7 @@
io.quarkus
- quarkus-devservices-common
+ quarkus-devservices-deployment
diff --git a/extensions/smallrye-reactive-messaging-amqp/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/amqp/deployment/AmqpDevServicesProcessor.java b/extensions/smallrye-reactive-messaging-amqp/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/amqp/deployment/AmqpDevServicesProcessor.java
index fe8de781906c7..2fcb397a450ba 100644
--- a/extensions/smallrye-reactive-messaging-amqp/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/amqp/deployment/AmqpDevServicesProcessor.java
+++ b/extensions/smallrye-reactive-messaging-amqp/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/amqp/deployment/AmqpDevServicesProcessor.java
@@ -2,6 +2,8 @@
import java.io.Closeable;
import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
@@ -14,12 +16,13 @@
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.DockerImageName;
+import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsDockerWorking;
import io.quarkus.deployment.IsNormal;
-import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.CuratedApplicationShutdownBuildItem;
-import io.quarkus.deployment.builditem.DevServicesConfigResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem.RunningDevService;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.console.ConsoleInstalledBuildItem;
import io.quarkus.deployment.console.StartupLogCompressor;
@@ -55,17 +58,16 @@ public class AmqpDevServicesProcessor {
private static final String DEFAULT_USER = "admin";
private static final String DEFAULT_PASSWORD = "admin";
- static volatile Closeable closeable;
+ static volatile RunningDevService devService;
static volatile AmqpDevServiceCfg cfg;
static volatile boolean first = true;
private final IsDockerWorking isDockerWorking = new IsDockerWorking(true);
@BuildStep(onlyIfNot = IsNormal.class, onlyIf = GlobalDevServicesConfig.Enabled.class)
- public DevServicesAmqpBrokerBuildItem startAmqpDevService(
+ public DevServicesResultBuildItem startAmqpDevService(
LaunchModeBuildItem launchMode,
AmqpBuildTimeConfig amqpClientBuildTimeConfig,
- BuildProducer devServicePropertiesProducer,
Optional consoleInstalledBuildItem,
CuratedApplicationShutdownBuildItem closeBuildItem,
LoggingSetupBuildItem loggingSetupBuildItem,
@@ -73,38 +75,30 @@ public DevServicesAmqpBrokerBuildItem startAmqpDevService(
AmqpDevServiceCfg configuration = getConfiguration(amqpClientBuildTimeConfig);
- if (closeable != null) {
+ if (devService != null) {
boolean shouldShutdownTheBroker = !configuration.equals(cfg);
if (!shouldShutdownTheBroker) {
- return null;
+ return devService.toBuildItem();
}
shutdownBroker();
cfg = null;
}
- AmqpBroker broker;
- DevServicesAmqpBrokerBuildItem artemis = null;
StartupLogCompressor compressor = new StartupLogCompressor(
(launchMode.isTest() ? "(test) " : "") + "AMQP Dev Services Starting:", consoleInstalledBuildItem,
loggingSetupBuildItem);
try {
- broker = startAmqpBroker(configuration, launchMode, devServicesConfig.timeout);
- if (broker != null) {
- closeable = broker.getCloseable();
- devServicePropertiesProducer.produce(new DevServicesConfigResultBuildItem(AMQP_HOST_PROP, broker.host));
- devServicePropertiesProducer
- .produce(new DevServicesConfigResultBuildItem(AMQP_PORT_PROP, Integer.toString(broker.port)));
- devServicePropertiesProducer.produce(new DevServicesConfigResultBuildItem(AMQP_USER_PROP, broker.user));
- devServicePropertiesProducer.produce(new DevServicesConfigResultBuildItem(AMQP_PASSWORD_PROP, broker.password));
-
- artemis = new DevServicesAmqpBrokerBuildItem(broker.host, broker.port, broker.user, broker.password);
-
- if (broker.isOwner()) {
+ RunningDevService newDevService = startAmqpBroker(configuration, launchMode, devServicesConfig.timeout);
+ if (newDevService != null) {
+ devService = newDevService;
+ Map config = devService.getConfig();
+ if (newDevService.isOwner()) {
log.info("Dev Services for AMQP started.");
log.infof("Other Quarkus applications in dev mode will find the "
+ "broker automatically. For Quarkus applications in production mode, you can connect to"
- + " this by starting your application with -Damqp.host=%s -Damqp.port=%d -Damqp.user=%s -Damqp.password=%s",
- broker.host, broker.port, broker.user, broker.password);
+ + " this by starting your application with -Damqp.host=%s -Damqp.port=%s -Damqp.user=%s -Damqp.password=%s",
+ config.get(AMQP_HOST_PROP), config.get(AMQP_PORT_PROP), config.get(AMQP_USER_PROP),
+ config.get(AMQP_PASSWORD_PROP));
}
}
compressor.close();
@@ -113,38 +107,43 @@ public DevServicesAmqpBrokerBuildItem startAmqpDevService(
throw new RuntimeException(t);
}
+ if (devService == null) {
+ return null;
+ }
+
// Configure the watch dog
if (first) {
first = false;
Runnable closeTask = () -> {
- if (closeable != null) {
+ if (devService != null) {
shutdownBroker();
log.info("Dev Services for AMQP shut down.");
}
first = true;
- closeable = null;
+ devService = null;
cfg = null;
};
closeBuildItem.addCloseTask(closeTask, true);
}
cfg = configuration;
- return artemis;
+ return devService.toBuildItem();
}
private void shutdownBroker() {
- if (closeable != null) {
+ if (devService != null) {
try {
- closeable.close();
+ devService.close();
} catch (Throwable e) {
log.error("Failed to stop the AMQP broker", e);
} finally {
- closeable = null;
+ devService = null;
}
}
}
- private AmqpBroker startAmqpBroker(AmqpDevServiceCfg config, LaunchModeBuildItem launchMode, Optional timeout) {
+ private RunningDevService startAmqpBroker(AmqpDevServiceCfg config, LaunchModeBuildItem launchMode,
+ Optional timeout) {
if (!config.devServicesEnabled) {
// explicitly disabled
log.debug("Not starting Dev Services for AMQP, as it has been disabled in the config.");
@@ -168,7 +167,7 @@ private AmqpBroker startAmqpBroker(AmqpDevServiceCfg config, LaunchModeBuildItem
return null;
}
- final Supplier defaultAmqpBrokerSupplier = () -> {
+ final Supplier defaultAmqpBrokerSupplier = () -> {
// Starting the broker
ArtemisContainer container = new ArtemisContainer(
DockerImageName.parse(config.imageName),
@@ -179,20 +178,24 @@ private AmqpBroker startAmqpBroker(AmqpDevServiceCfg config, LaunchModeBuildItem
timeout.ifPresent(container::withStartupTimeout);
container.start();
- return new AmqpBroker(
- container.getHost(),
- container.getPort(),
- DEFAULT_USER,
- DEFAULT_PASSWORD,
- container::close);
+ return getRunningService(container.getContainerId(), container::close, container.getHost(), container.getPort());
};
return amqpContainerLocator.locateContainer(config.serviceName, config.shared, launchMode.getLaunchMode())
- .map(containerAddress -> new AmqpBroker(containerAddress.getHost(), containerAddress.getPort(), "admin",
- "admin", null))
+ .map(containerAddress -> getRunningService(containerAddress.getId(), null, containerAddress.getHost(),
+ containerAddress.getPort()))
.orElseGet(defaultAmqpBrokerSupplier);
}
+ private RunningDevService getRunningService(String containerId, Closeable closeable, String host, int port) {
+ Map configMap = new HashMap<>();
+ configMap.put(AMQP_HOST_PROP, host);
+ configMap.put(AMQP_PORT_PROP, String.valueOf(port));
+ configMap.put(AMQP_USER_PROP, DEFAULT_USER);
+ configMap.put(AMQP_PASSWORD_PROP, DEFAULT_PASSWORD);
+ return new RunningDevService(Feature.SMALLRYE_REACTIVE_MESSAGING_AMQP.getName(), containerId, closeable, configMap);
+ }
+
private boolean hasAmqpChannelWithoutHostAndPort() {
Config config = ConfigProvider.getConfig();
for (String name : config.getPropertyNames()) {
@@ -220,30 +223,6 @@ private AmqpDevServiceCfg getConfiguration(AmqpBuildTimeConfig cfg) {
return new AmqpDevServiceCfg(devServicesConfig);
}
- private static class AmqpBroker {
- private final Closeable closeable;
- private final String host;
- private final int port;
- private final String user;
- private final String password;
-
- public AmqpBroker(String host, int port, String user, String password, Closeable closeable) {
- this.host = host;
- this.port = port;
- this.user = user;
- this.password = password;
- this.closeable = closeable;
- }
-
- public boolean isOwner() {
- return closeable != null;
- }
-
- public Closeable getCloseable() {
- return closeable;
- }
- }
-
private static final class AmqpDevServiceCfg {
private final boolean devServicesEnabled;
private final String imageName;
diff --git a/extensions/smallrye-reactive-messaging-amqp/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/amqp/deployment/DevServicesAmqpBrokerBuildItem.java b/extensions/smallrye-reactive-messaging-amqp/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/amqp/deployment/DevServicesAmqpBrokerBuildItem.java
deleted file mode 100644
index df1dd4b3931f6..0000000000000
--- a/extensions/smallrye-reactive-messaging-amqp/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/amqp/deployment/DevServicesAmqpBrokerBuildItem.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package io.quarkus.smallrye.reactivemessaging.amqp.deployment;
-
-import io.quarkus.builder.item.SimpleBuildItem;
-
-public final class DevServicesAmqpBrokerBuildItem extends SimpleBuildItem {
-
- public final String host;
- public final int port;
- public final String user;
- public final String password;
-
- public DevServicesAmqpBrokerBuildItem(String host, int port, String user, String password) {
- this.host = host;
- this.port = port;
- this.user = user;
- this.password = password;
- }
-}
diff --git a/extensions/smallrye-reactive-messaging-rabbitmq/deployment/pom.xml b/extensions/smallrye-reactive-messaging-rabbitmq/deployment/pom.xml
index 8684e27209aa5..a6f4d77ddebba 100644
--- a/extensions/smallrye-reactive-messaging-rabbitmq/deployment/pom.xml
+++ b/extensions/smallrye-reactive-messaging-rabbitmq/deployment/pom.xml
@@ -60,7 +60,7 @@
io.quarkus
- quarkus-devservices-common
+ quarkus-devservices-deployment
diff --git a/extensions/smallrye-reactive-messaging-rabbitmq/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/rabbitmq/deployment/DevServicesRabbitMQBrokerBuildItem.java b/extensions/smallrye-reactive-messaging-rabbitmq/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/rabbitmq/deployment/DevServicesRabbitMQBrokerBuildItem.java
deleted file mode 100644
index f3308b518288e..0000000000000
--- a/extensions/smallrye-reactive-messaging-rabbitmq/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/rabbitmq/deployment/DevServicesRabbitMQBrokerBuildItem.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package io.quarkus.smallrye.reactivemessaging.rabbitmq.deployment;
-
-import io.quarkus.builder.item.SimpleBuildItem;
-
-public final class DevServicesRabbitMQBrokerBuildItem extends SimpleBuildItem {
-
- public final String host;
- public final int port;
- public final String user;
- public final String password;
-
- public DevServicesRabbitMQBrokerBuildItem(String host, int port, String user, String password) {
- this.host = host;
- this.port = port;
- this.user = user;
- this.password = password;
- }
-}
diff --git a/extensions/smallrye-reactive-messaging-rabbitmq/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/rabbitmq/deployment/RabbitMQDevServicesProcessor.java b/extensions/smallrye-reactive-messaging-rabbitmq/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/rabbitmq/deployment/RabbitMQDevServicesProcessor.java
index cc0a4863a4100..d26beffffd4b1 100644
--- a/extensions/smallrye-reactive-messaging-rabbitmq/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/rabbitmq/deployment/RabbitMQDevServicesProcessor.java
+++ b/extensions/smallrye-reactive-messaging-rabbitmq/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/rabbitmq/deployment/RabbitMQDevServicesProcessor.java
@@ -3,6 +3,7 @@
import java.io.Closeable;
import java.time.Duration;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -19,11 +20,12 @@
import org.testcontainers.utility.DockerImageName;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
+import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsDockerWorking;
import io.quarkus.deployment.IsNormal;
-import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
-import io.quarkus.deployment.builditem.DevServicesConfigResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem;
+import io.quarkus.deployment.builditem.DevServicesResultBuildItem.RunningDevService;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.console.ConsoleInstalledBuildItem;
import io.quarkus.deployment.console.StartupLogCompressor;
@@ -54,57 +56,47 @@ public class RabbitMQDevServicesProcessor {
private static final String RABBITMQ_USERNAME_PROP = "rabbitmq-username";
private static final String RABBITMQ_PASSWORD_PROP = "rabbitmq-password";
- static volatile Closeable closeable;
+ static volatile RunningDevService devService;
static volatile RabbitMQDevServiceCfg cfg;
static volatile boolean first = true;
private final IsDockerWorking isDockerWorking = new IsDockerWorking(true);
@BuildStep(onlyIfNot = IsNormal.class, onlyIf = GlobalDevServicesConfig.Enabled.class)
- public DevServicesRabbitMQBrokerBuildItem startRabbitMQDevService(
+ public DevServicesResultBuildItem startRabbitMQDevService(
LaunchModeBuildItem launchMode,
RabbitMQBuildTimeConfig rabbitmqClientBuildTimeConfig,
- BuildProducer devServicePropertiesProducer,
Optional consoleInstalledBuildItem,
LoggingSetupBuildItem loggingSetupBuildItem,
GlobalDevServicesConfig devServicesConfig) {
RabbitMQDevServiceCfg configuration = getConfiguration(rabbitmqClientBuildTimeConfig);
- if (closeable != null) {
+ if (devService != null) {
boolean shouldShutdownTheBroker = !configuration.equals(cfg);
if (!shouldShutdownTheBroker) {
- return null;
+ return devService.toBuildItem();
}
shutdownBroker();
cfg = null;
}
- RabbitMQBroker broker;
- DevServicesRabbitMQBrokerBuildItem rabbitMQ = null;
StartupLogCompressor compressor = new StartupLogCompressor(
(launchMode.isTest() ? "(test) " : "") + "RabbitMQ Dev Services Starting:", consoleInstalledBuildItem,
loggingSetupBuildItem);
try {
- broker = startRabbitMQBroker(configuration, launchMode, devServicesConfig.timeout);
- if (broker != null) {
- closeable = broker.getCloseable();
- devServicePropertiesProducer.produce(new DevServicesConfigResultBuildItem(RABBITMQ_HOST_PROP, broker.host));
- devServicePropertiesProducer
- .produce(new DevServicesConfigResultBuildItem(RABBITMQ_PORT_PROP, Integer.toString(broker.port)));
- devServicePropertiesProducer
- .produce(new DevServicesConfigResultBuildItem(RABBITMQ_USERNAME_PROP, broker.username));
- devServicePropertiesProducer
- .produce(new DevServicesConfigResultBuildItem(RABBITMQ_PASSWORD_PROP, broker.password));
-
- rabbitMQ = new DevServicesRabbitMQBrokerBuildItem(broker.host, broker.port, broker.username, broker.password);
-
- if (broker.isOwner()) {
+ RunningDevService newDevService = startRabbitMQBroker(configuration, launchMode, devServicesConfig.timeout);
+ if (newDevService != null) {
+ devService = newDevService;
+
+ Map config = devService.getConfig();
+ if (devService.isOwner()) {
log.info("Dev Services for RabbitMQ started.");
log.infof("Other Quarkus applications in dev mode will find the "
+ "broker automatically. For Quarkus applications in production mode, you can connect to"
- + " this by starting your application with -Drabbitmq-host=%s -Drabbitmq-port=%d -Drabbitmq-username=%s -Drabbitmq-password=%s",
- broker.host, broker.port, broker.username, broker.password);
+ + " this by starting your application with -Drabbitmq-host=%s -Drabbitmq-port=%s -Drabbitmq-username=%s -Drabbitmq-password=%s",
+ config.get(RABBITMQ_HOST_PROP), config.get(RABBITMQ_PORT_PROP),
+ config.get(RABBITMQ_USERNAME_PROP), config.get(RABBITMQ_PASSWORD_PROP));
}
}
compressor.close();
@@ -113,39 +105,43 @@ public DevServicesRabbitMQBrokerBuildItem startRabbitMQDevService(
throw new RuntimeException(t);
}
+ if (devService == null) {
+ return null;
+ }
+
// Configure the watch dog
if (first) {
first = false;
Runnable closeTask = () -> {
- if (closeable != null) {
+ if (devService != null) {
shutdownBroker();
log.info("Dev Services for RabbitMQ shut down.");
}
first = true;
- closeable = null;
+ devService = null;
cfg = null;
};
QuarkusClassLoader cl = (QuarkusClassLoader) Thread.currentThread().getContextClassLoader();
((QuarkusClassLoader) cl.parent()).addCloseTask(closeTask);
}
cfg = configuration;
- return rabbitMQ;
+ return devService.toBuildItem();
}
private void shutdownBroker() {
- if (closeable != null) {
+ if (devService != null) {
try {
- closeable.close();
+ devService.close();
} catch (Throwable e) {
log.error("Failed to stop the RabbitMQ broker", e);
} finally {
- closeable = null;
+ devService = null;
}
}
}
- private RabbitMQBroker startRabbitMQBroker(RabbitMQDevServiceCfg config, LaunchModeBuildItem launchMode,
+ private RunningDevService startRabbitMQBroker(RabbitMQDevServiceCfg config, LaunchModeBuildItem launchMode,
Optional timeout) {
if (!config.devServicesEnabled) {
// explicitly disabled
@@ -180,25 +176,33 @@ private RabbitMQBroker startRabbitMQBroker(RabbitMQDevServiceCfg config, LaunchM
config.bindings
.forEach(b -> container.withBinding(b.source, b.destination, b.arguments, b.routingKey, b.destinationType));
- final Supplier defaultRabbitMQBrokerSupplier = () -> {
+ final Supplier defaultRabbitMQBrokerSupplier = () -> {
+
// Starting the broker
timeout.ifPresent(container::withStartupTimeout);
container.start();
- return new RabbitMQBroker(
- container.getHost(),
- container.getPort(),
- container.getAdminUsername(),
- container.getAdminPassword(),
- container::close);
+ return getRunningDevService(container.getContainerId(), container::close, container.getHost(),
+ container.getPort(), container.getAdminUsername(), container.getAdminPassword());
};
return rabbitmqContainerLocator.locateContainer(config.serviceName, config.shared, launchMode.getLaunchMode())
- .map(containerAddress -> new RabbitMQBroker(containerAddress.getHost(), containerAddress.getPort(),
- container.getAdminUsername(), container.getAdminPassword(), null))
+ .map(containerAddress -> getRunningDevService(containerAddress.getId(), null, containerAddress.getHost(),
+ containerAddress.getPort(), container.getAdminUsername(), container.getAdminPassword()))
.orElseGet(defaultRabbitMQBrokerSupplier);
}
+ private RunningDevService getRunningDevService(String containerId, Closeable closeable, String host, int port,
+ String username, String password) {
+ Map configMap = new HashMap<>();
+ configMap.put(RABBITMQ_HOST_PROP, host);
+ configMap.put(RABBITMQ_PORT_PROP, String.valueOf(port));
+ configMap.put(RABBITMQ_USERNAME_PROP, username);
+ configMap.put(RABBITMQ_PASSWORD_PROP, password);
+ return new RunningDevService(Feature.SMALLRYE_REACTIVE_MESSAGING_RABBITMQ.getName(),
+ containerId, closeable, configMap);
+ }
+
private boolean hasRabbitMQChannelWithoutHostAndPort() {
Config config = ConfigProvider.getConfig();
for (String name : config.getPropertyNames()) {
@@ -226,30 +230,6 @@ private RabbitMQDevServiceCfg getConfiguration(RabbitMQBuildTimeConfig cfg) {
return new RabbitMQDevServiceCfg(devServicesConfig);
}
- private static class RabbitMQBroker {
- private final Closeable closeable;
- private final String host;
- private final int port;
- private final String username;
- private final String password;
-
- public RabbitMQBroker(String host, int port, String username, String password, Closeable closeable) {
- this.host = host;
- this.port = port;
- this.username = username;
- this.password = password;
- this.closeable = closeable;
- }
-
- public boolean isOwner() {
- return closeable != null;
- }
-
- public Closeable getCloseable() {
- return closeable;
- }
- }
-
private static final class RabbitMQDevServiceCfg {
static class Exchange {
diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsoleProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsoleProcessor.java
index 8989a0946d91f..2d69afe056560 100644
--- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsoleProcessor.java
+++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsoleProcessor.java
@@ -53,6 +53,7 @@
import io.quarkus.deployment.builditem.WebSocketLogHandlerBuildItem;
import io.quarkus.deployment.console.ConsoleCommand;
import io.quarkus.deployment.console.ConsoleStateManager;
+import io.quarkus.deployment.dev.devservices.DevServiceDescriptionBuildItem;
import io.quarkus.deployment.ide.EffectiveIdeBuildItem;
import io.quarkus.deployment.ide.Ide;
import io.quarkus.deployment.logging.LoggingSetupBuildItem;
@@ -395,6 +396,11 @@ public WebJarBuildItem setupWebJar(
.build();
}
+ @BuildStep(onlyIf = { IsDevelopment.class })
+ public DevConsoleTemplateInfoBuildItem config(List serviceDescriptions) {
+ return new DevConsoleTemplateInfoBuildItem("devServices", serviceDescriptions);
+ }
+
@Record(ExecutionTime.RUNTIME_INIT)
@Consume(LoggingSetupBuildItem.class)
@BuildStep(onlyIf = IsDevelopment.class)
diff --git a/extensions/vertx-http/deployment/src/main/resources/dev-templates/index.html b/extensions/vertx-http/deployment/src/main/resources/dev-templates/index.html
index 97ace11515e0b..4debd03720629 100644
--- a/extensions/vertx-http/deployment/src/main/resources/dev-templates/index.html
+++ b/extensions/vertx-http/deployment/src/main/resources/dev-templates/index.html
@@ -17,6 +17,12 @@
Config Editor
+
+
+
+ Dev Services
+
+
diff --git a/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/dev-services.html b/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/dev-services.html
new file mode 100644
index 0000000000000..2d04b3c25c5b1
--- /dev/null
+++ b/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/dev-services.html
@@ -0,0 +1,65 @@
+{#include main fluid=true}
+{#style}
+table.inner > tbody > tr:nth-of-type(odd) {
+ background: transparent;
+}
+table.inner > tbody > tr:nth-of-type(even) {
+ background: transparent;
+}
+table.inner > tbody > tr > td {
+ padding: 5px;
+}
+{/style}
+{#title}Dev Services{/title}
+{#body}
+
+
+
+ Service |
+ Config |
+
+
+
+ {#for service in info:devServices}
+
+
+ {service.name}
+
+
+
+
+ Container |
+ {service.containerInfo.getShortId()} {service.containerInfo.formatNames()} |
+
+
+ Image |
+ {service.containerInfo.imageName} |
+
+
+ Networks |
+ {service.containerInfo.formatNetworks()} |
+
+
+ Exposed Ports |
+ {service.containerInfo.formatPorts()} |
+
+
+
+
+ |
+
+
+ {#for config in service.configs}
+
+ {config.key}
+ = {config.value}
+
+
+ {/for}
+ |
+
+ {/for}
+
+
+{/body}
+{/include}
\ No newline at end of file
diff --git a/integration-tests/devmode/src/test/java/io/quarkus/test/devconsole/DevConsoleDevServicesSmokeTest.java b/integration-tests/devmode/src/test/java/io/quarkus/test/devconsole/DevConsoleDevServicesSmokeTest.java
new file mode 100644
index 0000000000000..4776a45bba0f7
--- /dev/null
+++ b/integration-tests/devmode/src/test/java/io/quarkus/test/devconsole/DevConsoleDevServicesSmokeTest.java
@@ -0,0 +1,33 @@
+package io.quarkus.test.devconsole;
+
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.test.QuarkusDevModeTest;
+import io.restassured.RestAssured;
+
+/**
+ * Note that this test cannot be placed under the relevant {@code -deployment} module because then the DEV UI processor would
+ * not be able to locate the template resources correctly.
+ */
+public class DevConsoleDevServicesSmokeTest {
+
+ @RegisterExtension
+ static final QuarkusDevModeTest config = new QuarkusDevModeTest()
+ .withEmptyApplication();
+
+ @Test
+ public void testDevConsoleNotBroken() {
+ RestAssured.with()
+ .get("q/dev")
+ .then()
+ .statusCode(200).body(Matchers.containsString("Dev Services"));
+
+ RestAssured.with()
+ .get("q/dev/io.quarkus.quarkus-vertx-http/dev-services")
+ .then()
+ .statusCode(200).body(Matchers.containsString("Dev Services"));
+
+ }
+}