Skip to content

Commit 2809071

Browse files
Merge pull request #68 from wiremock/plugin-api
Introduce Plugin API and refactor extension management
2 parents 734ccc2 + 3403f76 commit 2809071

File tree

5 files changed

+123
-32
lines changed

5 files changed

+123
-32
lines changed

src/main/java/org/wiremock/integrations/testcontainers/WireMockContainer.java

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public class WireMockContainer extends GenericContainer<WireMockContainer> {
6565
private final StringBuilder wireMockArgs;
6666
private final Map<String, Stub> mappingStubs = new HashMap<>();
6767
private final Map<String, MountableFile> mappingFiles = new HashMap<>();
68-
private final Map<String, Extension> extensions = new HashMap<>();
68+
private final Map<String, WireMockPlugin> plugins = new HashMap<>();
6969
private boolean isBannerDisabled = true;
7070

7171
/**
@@ -268,28 +268,39 @@ public WireMockContainer withFileFromResource(Class<?> resource, String filename
268268
}
269269

270270
/**
271-
* Add extension that will be loaded from the specified JAR file.
272-
* @param id Unique ID of the extension, for logging purposes
271+
* Add extension that will be loaded from the specified JAR files.
272+
* In the internal engine, it will be handled as a single plugin.
273273
* @param classNames Class names of the extension to be included
274274
* @param jars JARs to be included into the container
275275
* @return this instance
276276
*/
277-
public WireMockContainer withExtension(String id, Collection<String> classNames, Collection<File> jars) {
278-
final Extension extension = new Extension(id);
279-
extension.extensionClassNames.addAll(classNames);
280-
extension.jars.addAll(jars);
281-
extensions.put(id, extension);
282-
return this;
277+
public WireMockContainer withExtensions(Collection<String> classNames, Collection<File> jars) {
278+
return withExtensions(WireMockPlugin.guessPluginId(classNames, jars), classNames, jars);
279+
}
280+
281+
/**
282+
* Add extension that will be loaded from the specified JAR files.
283+
* In the internal engine, it will be handled as a single plugin.
284+
* @param id Identifier top use
285+
* @param classNames Class names of the extension to be included
286+
* @param jars JARs to be included into the container
287+
* @return this instance
288+
*/
289+
public WireMockContainer withExtensions(String id, Collection<String> classNames, Collection<File> jars) {
290+
final WireMockPlugin extension = new WireMockPlugin(id)
291+
.withExtensions(classNames)
292+
.withJars(jars);
293+
return withPlugin(extension);
283294
}
284295

285296
/**
286297
* Add extension that will be loaded from the specified directory with JAR files.
287-
* @param id Unique ID of the extension, for logging purposes
298+
* In the internal engine, it will be handled as a single plugin.
288299
* @param classNames Class names of the extension to be included
289300
* @param jarDirectory Directory that stores all JARs
290301
* @return this instance
291302
*/
292-
public WireMockContainer withExtension(String id, Collection<String> classNames, File jarDirectory) {
303+
public WireMockContainer withExtensions(Collection<String> classNames, File jarDirectory) {
293304
final List<File> jarsInTheDirectory;
294305
try (Stream<Path> walk = Files.walk(jarDirectory.toPath())) {
295306
jarsInTheDirectory = walk
@@ -301,19 +312,28 @@ public WireMockContainer withExtension(String id, Collection<String> classNames,
301312
throw new IllegalArgumentException("Cannot list JARs in the directory " + jarDirectory, e);
302313
}
303314

304-
return withExtension(id, classNames, jarsInTheDirectory);
315+
return withExtensions(classNames, jarsInTheDirectory);
305316
}
306317

307318
/**
308319
* Add extension that will be loaded from the classpath.
309320
* This method can be used if the extension is a part of the WireMock bundle,
310-
* or a Jar is already added via {@link #withExtension(String, Collection, Collection)}}
311-
* @param id Unique ID of the extension, for logging purposes
321+
* or a Jar is already added via {@link #withExtensions(Collection, Collection)}}.
322+
* In the internal engine, it will be handled as a single plugin.
312323
* @param className Class name of the extension
313324
* @return this instance
314325
*/
315-
public WireMockContainer withExtension(String id, String className) {
316-
return withExtension(id, Collections.singleton(className), Collections.emptyList());
326+
public WireMockContainer withExtension(String className) {
327+
return withExtensions(Collections.singleton(className), Collections.emptyList());
328+
}
329+
330+
private WireMockContainer withPlugin(WireMockPlugin plugin) {
331+
String pluginId = plugin.getPluginId();
332+
if (plugins.containsKey(pluginId)) {
333+
throw new IllegalArgumentException("The plugin is already included: " + pluginId);
334+
}
335+
plugins.put(pluginId, plugin);
336+
return this;
317337
}
318338

319339
public String getBaseUrl() {
@@ -344,10 +364,10 @@ protected void configure() {
344364
}
345365

346366
final ArrayList<String> extensionClassNames = new ArrayList<>();
347-
for (Map.Entry<String, Extension> entry : extensions.entrySet()) {
348-
final Extension ext = entry.getValue();
349-
extensionClassNames.addAll(ext.extensionClassNames);
350-
for (File jar : ext.jars) {
367+
for (Map.Entry<String, WireMockPlugin> entry : plugins.entrySet()) {
368+
final WireMockPlugin ext = entry.getValue();
369+
extensionClassNames.addAll(ext.getExtensionClassNames());
370+
for (File jar : ext.getJars()) {
351371
withCopyToContainer(MountableFile.forHostPath(jar.toPath()), EXTENSIONS_DIR + jar.getName());
352372
}
353373
}
@@ -374,13 +394,5 @@ public Stub(String name, String json) {
374394
}
375395
}
376396

377-
private static final class Extension {
378-
final String id;
379-
final List<File> jars = new ArrayList<>();
380-
final List<String> extensionClassNames = new ArrayList<>();
381397

382-
public Extension(String id) {
383-
this.id = id;
384-
}
385-
}
386398
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package org.wiremock.integrations.testcontainers;
2+
3+
import org.testcontainers.shaded.com.google.common.io.Files;
4+
5+
import java.io.File;
6+
import java.util.ArrayList;
7+
import java.util.Collection;
8+
import java.util.Collections;
9+
import java.util.List;
10+
import java.util.Optional;
11+
import java.util.Random;
12+
13+
/**
14+
* Unofficial notion of a WirteMock plugin.
15+
* WireMock at the moment operates only on the extension level,
16+
* and here we try to introduce a concept of a plugin that may span multiple JARs and extensions.
17+
* {@link #extensionClassNames} may be empty for WireMock 3 that supports auto-loading
18+
*/
19+
/*package*/ class WireMockPlugin {
20+
private final String pluginId;
21+
private final List<File> jars = new ArrayList<>();
22+
private final List<String> extensionClassNames = new ArrayList<>();
23+
24+
public WireMockPlugin(String id) {
25+
this.pluginId = id;
26+
}
27+
28+
public String getPluginId() {
29+
return pluginId;
30+
}
31+
32+
public WireMockPlugin withJars(Collection<File> jars) {
33+
this.jars.addAll(jars);
34+
return this;
35+
}
36+
37+
public WireMockPlugin withJar(File jar) {
38+
return withJars(Collections.singleton(jar));
39+
}
40+
41+
public WireMockPlugin withExtensions(Collection<String> extensionClassNames) {
42+
this.extensionClassNames.addAll(extensionClassNames);
43+
return this;
44+
}
45+
46+
public WireMockPlugin withExtension(String className) {
47+
return withExtensions(Collections.singleton(className));
48+
}
49+
50+
/**
51+
* Get JARs associated with the extension
52+
* @return List of JARs. Might be empty if the plugin/extension is a part of the WireMock core or already in the classpath
53+
*/
54+
public List<File> getJars() {
55+
return jars;
56+
}
57+
58+
/**
59+
* Get the list of extensions. Might be empty in WireMock 3
60+
* @return List of extension class names within the plugin
61+
*/
62+
public List<String> getExtensionClassNames() {
63+
return extensionClassNames;
64+
}
65+
66+
public static String guessPluginId(Collection<String> classNames, Collection<File> jars) {
67+
File jar = jars.stream().findFirst().orElse(null);
68+
if (jar != null) {
69+
return Files.getNameWithoutExtension(jar.getName());
70+
}
71+
72+
String className = classNames.stream().findFirst().orElse(null);
73+
if (className != null && className.length() > 1) {
74+
return className.substring(className.lastIndexOf('.') + 1);
75+
}
76+
77+
return "plugin_" + Math.random(); // Double is fun, right? :)
78+
}
79+
}

src/test/java/org/wiremock/integrations/testcontainers/WireMockContainerExtensionTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class WireMockContainerExtensionTest {
4444
.withLogConsumer(new Slf4jLogConsumer(LOGGER))
4545
.withStartupTimeout(Duration.ofSeconds(60))
4646
.withMapping("json-body-transformer", WireMockContainerExtensionTest.class, "json-body-transformer.json")
47-
.withExtension("JSON Body Transformer",
47+
.withExtensions("JSON Body Transformer",
4848
Collections.singleton("com.ninecookies.wiremock.extensions.JsonBodyTransformer"),
4949
Collections.singleton(Paths.get("target", "test-wiremock-extension", "wiremock-extensions-0.4.1-jar-with-dependencies.jar").toFile()));
5050

src/test/java/org/wiremock/integrations/testcontainers/WireMockContainerExtensionsCombinationTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ class WireMockContainerExtensionsCombinationTest {
4242
WireMockContainer wiremockServer = new WireMockContainer(WireMockContainer.WIREMOCK_2_LATEST)
4343
.withLogConsumer(new Slf4jLogConsumer(LOGGER))
4444
.withMapping("json-body-transformer", WireMockContainerExtensionsCombinationTest.class, "json-body-transformer.json")
45-
.withExtension("Webhook",
45+
.withExtensions("Webhook",
4646
Collections.singleton("org.wiremock.webhooks.Webhooks"),
4747
Collections.singleton(Paths.get("target", "test-wiremock-extension", "wiremock-webhooks-extension-2.35.0.jar").toFile()))
48-
.withExtension("JSON Body Transformer",
48+
.withExtensions("JSON Body Transformer",
4949
Collections.singleton("com.ninecookies.wiremock.extensions.JsonBodyTransformer"),
5050
Collections.singleton(Paths.get("target", "test-wiremock-extension", "wiremock-extensions-0.4.1-jar-with-dependencies.jar").toFile()));
5151

src/test/java/org/wiremock/integrations/testcontainers/WireMockContainerExtensionsWebhookTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class WireMockContainerExtensionsWebhookTest {
6060
.withLogConsumer(new Slf4jLogConsumer(LOGGER))
6161
.withCliArg("--global-response-templating")
6262
.withMapping("webhook-callback-template", WireMockContainerExtensionsWebhookTest.class, "webhook-callback-template.json")
63-
.withExtension("Webhook",
63+
.withExtensions("Webhook",
6464
Collections.singleton("org.wiremock.webhooks.Webhooks"),
6565
Collections.singleton(Paths.get("target", "test-wiremock-extension", "wiremock-webhooks-extension-2.35.0.jar").toFile()))
6666
.withAccessToHost(true); // Force the host access mechanism

0 commit comments

Comments
 (0)