diff --git a/src/main/java/org/wiremock/integrations/testcontainers/WireMockContainer.java b/src/main/java/org/wiremock/integrations/testcontainers/WireMockContainer.java index 5c96d65..a93c2dc 100644 --- a/src/main/java/org/wiremock/integrations/testcontainers/WireMockContainer.java +++ b/src/main/java/org/wiremock/integrations/testcontainers/WireMockContainer.java @@ -65,7 +65,7 @@ public class WireMockContainer extends GenericContainer { private final StringBuilder wireMockArgs; private final Map mappingStubs = new HashMap<>(); private final Map mappingFiles = new HashMap<>(); - private final Map extensions = new HashMap<>(); + private final Map plugins = new HashMap<>(); private boolean isBannerDisabled = true; /** @@ -169,28 +169,39 @@ public WireMockContainer withFileFromResource(String name, Class resource, St } /** - * Add extension that will be loaded from the specified JAR file. - * @param id Unique ID of the extension, for logging purposes + * Add extension that will be loaded from the specified JAR files. + * In the internal engine, it will be handled as a single plugin. * @param classNames Class names of the extension to be included * @param jars JARs to be included into the container * @return this instance */ - public WireMockContainer withExtension(String id, Collection classNames, Collection jars) { - final Extension extension = new Extension(id); - extension.extensionClassNames.addAll(classNames); - extension.jars.addAll(jars); - extensions.put(id, extension); - return this; + public WireMockContainer withExtensions(Collection classNames, Collection jars) { + return withExtensions(WireMockPlugin.guessPluginId(classNames, jars), classNames, jars); + } + + /** + * Add extension that will be loaded from the specified JAR files. + * In the internal engine, it will be handled as a single plugin. + * @param id Identifier top use + * @param classNames Class names of the extension to be included + * @param jars JARs to be included into the container + * @return this instance + */ + public WireMockContainer withExtensions(String id, Collection classNames, Collection jars) { + final WireMockPlugin extension = new WireMockPlugin(id) + .withExtensions(classNames) + .withJars(jars); + return withPlugin(extension); } /** * Add extension that will be loaded from the specified directory with JAR files. - * @param id Unique ID of the extension, for logging purposes + * In the internal engine, it will be handled as a single plugin. * @param classNames Class names of the extension to be included * @param jarDirectory Directory that stores all JARs * @return this instance */ - public WireMockContainer withExtension(String id, Collection classNames, File jarDirectory) { + public WireMockContainer withExtensions(Collection classNames, File jarDirectory) { final List jarsInTheDirectory; try (Stream walk = Files.walk(jarDirectory.toPath())) { jarsInTheDirectory = walk @@ -202,19 +213,28 @@ public WireMockContainer withExtension(String id, Collection classNames, throw new IllegalArgumentException("Cannot list JARs in the directory " + jarDirectory, e); } - return withExtension(id, classNames, jarsInTheDirectory); + return withExtensions(classNames, jarsInTheDirectory); } /** * Add extension that will be loaded from the classpath. * This method can be used if the extension is a part of the WireMock bundle, - * or a Jar is already added via {@link #withExtension(String, Collection, Collection)}} - * @param id Unique ID of the extension, for logging purposes + * or a Jar is already added via {@link #withExtensions(Collection, Collection)}}. + * In the internal engine, it will be handled as a single plugin. * @param className Class name of the extension * @return this instance */ - public WireMockContainer withExtension(String id, String className) { - return withExtension(id, Collections.singleton(className), Collections.emptyList()); + public WireMockContainer withExtension(String className) { + return withExtensions(Collections.singleton(className), Collections.emptyList()); + } + + private WireMockContainer withPlugin(WireMockPlugin plugin) { + String pluginId = plugin.getPluginId(); + if (plugins.containsKey(pluginId)) { + throw new IllegalArgumentException("The plugin is already included: " + pluginId); + } + plugins.put(pluginId, plugin); + return this; } public String getBaseUrl() { @@ -245,10 +265,10 @@ protected void configure() { } final ArrayList extensionClassNames = new ArrayList<>(); - for (Map.Entry entry : extensions.entrySet()) { - final Extension ext = entry.getValue(); - extensionClassNames.addAll(ext.extensionClassNames); - for (File jar : ext.jars) { + for (Map.Entry entry : plugins.entrySet()) { + final WireMockPlugin ext = entry.getValue(); + extensionClassNames.addAll(ext.getExtensionClassNames()); + for (File jar : ext.getJars()) { withCopyToContainer(MountableFile.forHostPath(jar.toPath()), EXTENSIONS_DIR + jar.getName()); } } @@ -275,13 +295,5 @@ public Stub(String name, String json) { } } - private static final class Extension { - final String id; - final List jars = new ArrayList<>(); - final List extensionClassNames = new ArrayList<>(); - public Extension(String id) { - this.id = id; - } - } } diff --git a/src/main/java/org/wiremock/integrations/testcontainers/WireMockPlugin.java b/src/main/java/org/wiremock/integrations/testcontainers/WireMockPlugin.java new file mode 100644 index 0000000..3a39ae7 --- /dev/null +++ b/src/main/java/org/wiremock/integrations/testcontainers/WireMockPlugin.java @@ -0,0 +1,79 @@ +package org.wiremock.integrations.testcontainers; + +import org.testcontainers.shaded.com.google.common.io.Files; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Random; + +/** + * Unofficial notion of a WirteMock plugin. + * WireMock at the moment operates only on the extension level, + * and here we try to introduce a concept of a plugin that may span multiple JARs and extensions. + * {@link #extensionClassNames} may be empty for WireMock 3 that supports auto-loading + */ + /*package*/ class WireMockPlugin { + private final String pluginId; + private final List jars = new ArrayList<>(); + private final List extensionClassNames = new ArrayList<>(); + + public WireMockPlugin(String id) { + this.pluginId = id; + } + + public String getPluginId() { + return pluginId; + } + + public WireMockPlugin withJars(Collection jars) { + this.jars.addAll(jars); + return this; + } + + public WireMockPlugin withJar(File jar) { + return withJars(Collections.singleton(jar)); + } + + public WireMockPlugin withExtensions(Collection extensionClassNames) { + this.extensionClassNames.addAll(extensionClassNames); + return this; + } + + public WireMockPlugin withExtension(String className) { + return withExtensions(Collections.singleton(className)); + } + + /** + * Get JARs associated with the extension + * @return List of JARs. Might be empty if the plugin/extension is a part of the WireMock core or already in the classpath + */ + public List getJars() { + return jars; + } + + /** + * Get the list of extensions. Might be empty in WireMock 3 + * @return List of extension class names within the plugin + */ + public List getExtensionClassNames() { + return extensionClassNames; + } + + public static String guessPluginId(Collection classNames, Collection jars) { + File jar = jars.stream().findFirst().orElse(null); + if (jar != null) { + return Files.getNameWithoutExtension(jar.getName()); + } + + String className = classNames.stream().findFirst().orElse(null); + if (className != null && className.length() > 1) { + return className.substring(className.lastIndexOf('.') + 1); + } + + return "plugin_" + Math.random(); // Double is fun, right? :) + } +} diff --git a/src/test/java/org/wiremock/integrations/testcontainers/WireMockContainerExtensionTest.java b/src/test/java/org/wiremock/integrations/testcontainers/WireMockContainerExtensionTest.java index 440269c..4504510 100644 --- a/src/test/java/org/wiremock/integrations/testcontainers/WireMockContainerExtensionTest.java +++ b/src/test/java/org/wiremock/integrations/testcontainers/WireMockContainerExtensionTest.java @@ -44,7 +44,7 @@ class WireMockContainerExtensionTest { .withLogConsumer(new Slf4jLogConsumer(LOGGER)) .withStartupTimeout(Duration.ofSeconds(60)) .withMapping("json-body-transformer", WireMockContainerExtensionTest.class, "json-body-transformer.json") - .withExtension("JSON Body Transformer", + .withExtensions("JSON Body Transformer", Collections.singleton("com.ninecookies.wiremock.extensions.JsonBodyTransformer"), Collections.singleton(Paths.get("target", "test-wiremock-extension", "wiremock-extensions-0.4.1-jar-with-dependencies.jar").toFile())); diff --git a/src/test/java/org/wiremock/integrations/testcontainers/WireMockContainerExtensionsCombinationTest.java b/src/test/java/org/wiremock/integrations/testcontainers/WireMockContainerExtensionsCombinationTest.java index 353b019..7d85451 100644 --- a/src/test/java/org/wiremock/integrations/testcontainers/WireMockContainerExtensionsCombinationTest.java +++ b/src/test/java/org/wiremock/integrations/testcontainers/WireMockContainerExtensionsCombinationTest.java @@ -42,10 +42,10 @@ class WireMockContainerExtensionsCombinationTest { WireMockContainer wiremockServer = new WireMockContainer(WireMockContainer.WIREMOCK_2_LATEST) .withLogConsumer(new Slf4jLogConsumer(LOGGER)) .withMapping("json-body-transformer", WireMockContainerExtensionsCombinationTest.class, "json-body-transformer.json") - .withExtension("Webhook", + .withExtensions("Webhook", Collections.singleton("org.wiremock.webhooks.Webhooks"), Collections.singleton(Paths.get("target", "test-wiremock-extension", "wiremock-webhooks-extension-2.35.0.jar").toFile())) - .withExtension("JSON Body Transformer", + .withExtensions("JSON Body Transformer", Collections.singleton("com.ninecookies.wiremock.extensions.JsonBodyTransformer"), Collections.singleton(Paths.get("target", "test-wiremock-extension", "wiremock-extensions-0.4.1-jar-with-dependencies.jar").toFile())); diff --git a/src/test/java/org/wiremock/integrations/testcontainers/WireMockContainerExtensionsWebhookTest.java b/src/test/java/org/wiremock/integrations/testcontainers/WireMockContainerExtensionsWebhookTest.java index 0428f6b..ba54e21 100644 --- a/src/test/java/org/wiremock/integrations/testcontainers/WireMockContainerExtensionsWebhookTest.java +++ b/src/test/java/org/wiremock/integrations/testcontainers/WireMockContainerExtensionsWebhookTest.java @@ -60,7 +60,7 @@ class WireMockContainerExtensionsWebhookTest { .withLogConsumer(new Slf4jLogConsumer(LOGGER)) .withCliArg("--global-response-templating") .withMapping("webhook-callback-template", WireMockContainerExtensionsWebhookTest.class, "webhook-callback-template.json") - .withExtension("Webhook", + .withExtensions("Webhook", Collections.singleton("org.wiremock.webhooks.Webhooks"), Collections.singleton(Paths.get("target", "test-wiremock-extension", "wiremock-webhooks-extension-2.35.0.jar").toFile())) .withAccessToHost(true); // Force the host access mechanism