io.quarkiverse.wiremock.devservice.WireMockServerProcessor Maven / Gradle / Ivy
package io.quarkiverse.wiremock.devservice;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static io.quarkiverse.wiremock.devservice.WireMockConfigKey.PORT;
import static java.lang.String.format;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jboss.logging.Logger;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.common.ClasspathFileSource;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Consume;
import io.quarkus.deployment.builditem.CuratedApplicationShutdownBuildItem;
import io.quarkus.deployment.builditem.DevServicesResultBuildItem;
import io.quarkus.deployment.builditem.DevServicesResultBuildItem.RunningDevService;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.HotDeploymentWatchedFileBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.builditem.LiveReloadBuildItem;
import io.quarkus.deployment.dev.devservices.DevServiceDescriptionBuildItem;
import io.quarkus.deployment.dev.devservices.GlobalDevServicesConfig;
import io.quarkus.runtime.configuration.ConfigurationException;
class WireMockServerProcessor {
private static final Logger LOGGER = Logger.getLogger(WireMockServerProcessor.class);
private static final String FEATURE_NAME = "wiremock";
private static final String DEV_SERVICE_NAME = "WireMock";
private static final String MAPPINGS = "mappings";
private static final String FILES = "__files";
private static final int MIN_PORT = 1025;
private static final int MAX_PORT = 65535;
static volatile RunningDevService devService;
@BuildStep
FeatureBuildItem feature() {
return new FeatureBuildItem(FEATURE_NAME);
}
@BuildStep(onlyIf = { WireMockServerEnabled.class, GlobalDevServicesConfig.Enabled.class })
DevServicesResultBuildItem setup(LaunchModeBuildItem launchMode, LiveReloadBuildItem liveReload,
CuratedApplicationShutdownBuildItem shutdown, WireMockServerBuildTimeConfig config,
BuildProducer configErrors) {
LOGGER.debugf("Quarkus launch mode [%s]", launchMode.getLaunchMode());
if (isPortConfigInvalid(config)) {
configErrors.produce(new ValidationErrorBuildItem(new ConfigurationException(
format("The specified port %d is not part of the permitted port range! Please specify a port between %d and %d.",
config.port().getAsInt(), MIN_PORT, MAX_PORT))));
return null;
}
// register shutdown callback once
shutdown.addCloseTask(WireMockServerProcessor::stopWireMockDevService, true);
if (liveReload.isLiveReload() && config.reload()) {
LOGGER.debug("Live reload triggered!");
stopWireMockDevService();
}
if (devService == null) {
devService = startWireMockDevService(config);
}
return devService.toBuildItem();
}
@BuildStep(onlyIf = { WireMockServerEnabled.class, GlobalDevServicesConfig.Enabled.class })
@Consume(DevServicesResultBuildItem.class)
DevServiceDescriptionBuildItem renderDevServiceDevUICard() {
return new DevServiceDescriptionBuildItem(DEV_SERVICE_NAME, null, devService.getConfig());
}
@BuildStep(onlyIf = { WireMockServerEnabled.class, GlobalDevServicesConfig.Enabled.class, IsDevelopment.class })
void watchWireMockConfigFiles(WireMockServerBuildTimeConfig config,
BuildProducer items) {
if (!config.isClasspathFilesMapping()) {
listFiles(Paths.get(config.effectiveFileMapping(), MAPPINGS), Paths.get(config.effectiveFileMapping(), FILES))
.forEach(file -> {
LOGGER.debugf("Watching [%s] for hot deployment!", file);
items.produce(new HotDeploymentWatchedFileBuildItem(file));
});
}
}
private static RunningDevService startWireMockDevService(WireMockServerBuildTimeConfig config) {
final WireMockConfiguration configuration = options()
.globalTemplating(config.globalResponseTemplating())
.extensionScanningEnabled(config.extensionScanningEnabled());
config.port().ifPresentOrElse(configuration::port, configuration::dynamicPort);
if (config.isClasspathFilesMapping()) {
configuration
.fileSource(new ClasspathFileSource(Thread.currentThread().getContextClassLoader(),
config.effectiveFileMapping()));
} else {
configuration.usingFilesUnderDirectory(config.effectiveFileMapping());
}
final WireMockServer server = new WireMockServer(configuration);
server.start();
LOGGER.debugf("WireMock server listening on port [%s]", server.port());
return new RunningDevService(DEV_SERVICE_NAME, null, server::shutdown, PORT, String.valueOf(server.port()));
}
private static synchronized void stopWireMockDevService() {
try {
if (devService != null) {
LOGGER.debugf("Stopping WireMock server running on port %s", devService.getConfig().get(PORT));
devService.close();
}
} catch (IOException e) {
LOGGER.error("Failed to stop WireMock server", e);
throw new UncheckedIOException(e);
} finally {
devService = null;
}
}
private static boolean isPortConfigInvalid(WireMockServerBuildTimeConfig config) {
if (config.port().isEmpty()) {
return false;
}
final int port = config.port().getAsInt();
return port < MIN_PORT || port > MAX_PORT;
}
private static Set listFiles(Path... dirs) {
return Arrays.stream(dirs).filter(Files::isDirectory).map(WireMockServerProcessor::fileWalk)
.flatMap(Set::stream).collect(Collectors.toSet());
}
private static Set fileWalk(Path start) {
try (Stream stream = Files.walk(start)) {
return stream.filter(Files::isRegularFile).map(Path::toAbsolutePath).map(Path::toString)
.collect(Collectors.toSet());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy