Skip to content

Commit a7d594a

Browse files
author
Tom Akehurst
committed
Added ability to load extensions via ServiceLoader, both from factories and directly
1 parent 10aa867 commit a7d594a

17 files changed

+269
-59
lines changed

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ dependencies {
132132
}
133133
testImplementation "io.netty:netty-all:4.1.93.Final"
134134

135+
testImplementation files('test-extension/test-extension.jar')
136+
135137
constraints {
136138
implementation "net.minidev:json-smart:2.4.11", {
137139
because 'Pinning this above the transitive version from json-path to get CVE fix'

src/main/java/com/github/tomakehurst/wiremock/core/WireMockConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ public WireMockConfiguration extensions(Extension... extensionInstances) {
411411
return this;
412412
}
413413

414-
public WireMockConfiguration extensions(ExtensionFactory<?>... extensionFactories) {
414+
public WireMockConfiguration extensions(ExtensionFactory... extensionFactories) {
415415
extensions.add(extensionFactories);
416416
return this;
417417
}

src/main/java/com/github/tomakehurst/wiremock/extension/ExtensionDeclarations.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public class ExtensionDeclarations {
2424
private final List<String> classNames;
2525
private final List<Class<? extends Extension>> classes;
2626
private final Map<String, Extension> instances;
27-
private final List<ExtensionFactory<? extends Extension>> factories;
27+
private final List<ExtensionFactory> factories;
2828

2929
public ExtensionDeclarations() {
3030
this.classNames = new ArrayList<>();
@@ -45,7 +45,7 @@ public void add(Class<? extends Extension>... classes) {
4545
this.classes.addAll(asList(classes));
4646
}
4747

48-
public void add(ExtensionFactory<? extends Extension>... factories) {
48+
public void add(ExtensionFactory... factories) {
4949
this.factories.addAll(asList(factories));
5050
}
5151

@@ -61,7 +61,7 @@ public Map<String, Extension> getInstances() {
6161
return instances;
6262
}
6363

64-
public List<ExtensionFactory<? extends Extension>> getFactories() {
64+
public List<ExtensionFactory> getFactories() {
6565
return factories;
6666
}
6767
}

src/main/java/com/github/tomakehurst/wiremock/extension/ExtensionFactory.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package com.github.tomakehurst.wiremock.extension;
1717

18-
public interface ExtensionFactory<T extends Extension> {
19-
T create(WireMockServices services);
18+
import java.util.List;
19+
20+
public interface ExtensionFactory {
21+
List<Extension> create(WireMockServices services);
2022
}

src/main/java/com/github/tomakehurst/wiremock/extension/Extensions.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,9 @@
2828
import com.github.tomakehurst.wiremock.store.Stores;
2929
import com.google.common.collect.Maps;
3030
import com.google.common.collect.Streams;
31-
import java.util.LinkedHashMap;
32-
import java.util.Map;
33-
import java.util.Set;
31+
import java.util.*;
3432
import java.util.function.Function;
33+
import java.util.stream.Stream;
3534

3635
public class Extensions implements WireMockServices {
3736

@@ -80,14 +79,32 @@ public void load() {
8079
});
8180

8281
loadedExtensions.putAll(extensionDeclarations.getInstances());
82+
83+
loadedExtensions.putAll(
84+
loadExtensionsAsServices().collect(toMap(Extension::getName, Function.identity())));
85+
86+
final Stream<ExtensionFactory> allFactories =
87+
Streams.concat(
88+
extensionDeclarations.getFactories().stream(), loadExtensionFactoriesAsServices());
8389
loadedExtensions.putAll(
84-
extensionDeclarations.getFactories().stream()
90+
allFactories
8591
.map(factory -> factory.create(Extensions.this))
92+
.flatMap(List::stream)
8693
.collect(toMap(Extension::getName, Function.identity())));
8794

8895
configureTemplating();
8996
}
9097

98+
private Stream<Extension> loadExtensionsAsServices() {
99+
final ServiceLoader<Extension> loader = ServiceLoader.load(Extension.class);
100+
return loader.stream().map(ServiceLoader.Provider::get);
101+
}
102+
103+
private Stream<ExtensionFactory> loadExtensionFactoriesAsServices() {
104+
final ServiceLoader<ExtensionFactory> loader = ServiceLoader.load(ExtensionFactory.class);
105+
return loader.stream().map(ServiceLoader.Provider::get);
106+
}
107+
91108
private void configureTemplating() {
92109
final Map<String, Helper<?>> helpers =
93110
ofType(TemplateHelperProviderExtension.class).values().stream()

src/main/java/com/github/tomakehurst/wiremock/stubbing/AbstractStubMappings.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@
1616
package com.github.tomakehurst.wiremock.stubbing;
1717

1818
import static com.github.tomakehurst.wiremock.common.LocalNotifier.notifier;
19+
import static com.github.tomakehurst.wiremock.common.Pair.pair;
1920
import static com.github.tomakehurst.wiremock.http.ResponseDefinition.copyOf;
2021
import static com.google.common.base.MoreObjects.firstNonNull;
2122
import static java.util.stream.Collectors.toList;
2223

2324
import com.github.tomakehurst.wiremock.admin.NotFoundException;
2425
import com.github.tomakehurst.wiremock.common.FileSource;
2526
import com.github.tomakehurst.wiremock.common.Json;
27+
import com.github.tomakehurst.wiremock.common.Pair;
2628
import com.github.tomakehurst.wiremock.extension.Parameters;
2729
import com.github.tomakehurst.wiremock.extension.ResponseDefinitionTransformer;
2830
import com.github.tomakehurst.wiremock.extension.ResponseDefinitionTransformerV2;
@@ -94,8 +96,11 @@ public ServeEvent serveFor(ServeEvent initialServeEvent) {
9496
initialServeEvent
9597
.withStubMapping(matchingMapping)
9698
.withResponseDefinition(responseDefinition);
97-
responseDefinition =
99+
100+
final Pair<ServeEvent, ResponseDefinition> transformed =
98101
applyV2Transformations(serveEvent, ImmutableList.copyOf(v2transformers.values()));
102+
serveEvent = transformed.a;
103+
responseDefinition = transformed.b;
99104

100105
return serveEvent.withResponseDefinition(copyOf(responseDefinition));
101106
}
@@ -122,13 +127,13 @@ private ResponseDefinition applyV1Transformations(
122127
request, newResponseDef, transformers.subList(1, transformers.size()));
123128
}
124129

125-
private ResponseDefinition applyV2Transformations(
130+
private Pair<ServeEvent, ResponseDefinition> applyV2Transformations(
126131
ServeEvent serveEvent, List<ResponseDefinitionTransformerV2> transformers) {
127132

128133
final ResponseDefinition responseDefinition = serveEvent.getResponseDefinition();
129134

130135
if (transformers.isEmpty()) {
131-
return responseDefinition;
136+
return pair(serveEvent, responseDefinition);
132137
}
133138

134139
ResponseDefinitionTransformerV2 transformer = transformers.get(0);

src/test/java/com/github/tomakehurst/wiremock/ExtensionFactoryTest.java

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@
1515
*/
1616
package com.github.tomakehurst.wiremock;
1717

18+
import static com.github.tomakehurst.wiremock.client.WireMock.get;
19+
import static com.github.tomakehurst.wiremock.client.WireMock.noContent;
1820
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
1921
import static com.github.tomakehurst.wiremock.http.RequestMethod.GET;
2022
import static com.github.tomakehurst.wiremock.testsupport.TestFiles.defaultTestFilesRoot;
2123
import static net.javacrumbs.jsonunit.JsonMatchers.*;
2224
import static org.hamcrest.MatcherAssert.assertThat;
2325
import static org.hamcrest.Matchers.endsWith;
26+
import static org.hamcrest.Matchers.is;
2427

2528
import com.github.tomakehurst.wiremock.admin.Router;
2629
import com.github.tomakehurst.wiremock.common.FileSource;
@@ -33,6 +36,7 @@
3336
import com.github.tomakehurst.wiremock.http.ResponseDefinition;
3437
import com.github.tomakehurst.wiremock.store.Stores;
3538
import com.github.tomakehurst.wiremock.testsupport.WireMockTestClient;
39+
import java.util.List;
3640
import java.util.Map;
3741
import org.junit.jupiter.api.AfterEach;
3842
import org.junit.jupiter.api.Test;
@@ -59,17 +63,14 @@ void injectsCoreServicesOnConstructionByFactory() {
5963
.templatingEnabled(false)
6064
.extensions(
6165
services ->
62-
new MiscInfoApi(
63-
services.getAdmin(),
64-
services.getOptions(),
65-
services.getStores(),
66-
services.getFiles(),
67-
services.getExtensions())));
68-
69-
assertCorrectValuesReturnedFromApiCall();
70-
}
66+
List.of(
67+
new MiscInfoApi(
68+
services.getAdmin(),
69+
services.getOptions(),
70+
services.getStores(),
71+
services.getFiles(),
72+
services.getExtensions()))));
7173

72-
private void assertCorrectValuesReturnedFromApiCall() {
7374
client.get("/something");
7475
client.get("/something");
7576

@@ -79,7 +80,31 @@ private void assertCorrectValuesReturnedFromApiCall() {
7980
assertThat(content, jsonPartMatches("fileSourcePath", endsWith("test-file-root/__files")));
8081
assertThat(content, jsonPartEquals("requestCount", 2));
8182
assertThat(content, jsonPartEquals("stubCorsEnabled", true));
82-
assertThat(content, jsonPartEquals("extensionCount", 1));
83+
assertThat(
84+
content, jsonPartEquals("extensionCount", 3)); // Includes the two service loaded extensions
85+
}
86+
87+
@Test
88+
void usesExtensionFactoryLoadedViaServiceLoader() {
89+
initialiseWireMockServer(
90+
options().dynamicPort().withRootDirectory(defaultTestFilesRoot()).templatingEnabled(false));
91+
92+
wm.stubFor(get("/transform-this").willReturn(noContent().withTransformers("loader-test")));
93+
94+
client.get("/just-count-this");
95+
96+
assertThat(client.get("/transform-this").content(), is("Request count 1"));
97+
}
98+
99+
@Test
100+
void usesExtensionInstanceLoadedViaServiceLoader() {
101+
initialiseWireMockServer(
102+
options().dynamicPort().withRootDirectory(defaultTestFilesRoot()).templatingEnabled(false));
103+
104+
wm.stubFor(
105+
get("/transform-this").willReturn(noContent().withTransformers("instance-loader-test")));
106+
107+
assertThat(client.get("/transform-this").content(), is("Expected stuff"));
83108
}
84109

85110
private void initialiseWireMockServer(WireMockConfiguration options) {

src/test/java/com/github/tomakehurst/wiremock/ResponseTransformerV2AcceptanceTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import com.github.tomakehurst.wiremock.http.Response;
3131
import com.github.tomakehurst.wiremock.stubbing.ServeEvent;
3232
import com.github.tomakehurst.wiremock.testsupport.WireMockTestClient;
33+
import java.util.List;
3334
import org.junit.jupiter.api.Test;
3435

3536
public class ResponseTransformerV2AcceptanceTest {
@@ -74,7 +75,8 @@ public void globalTransformAppliedWithLocalParameters() {
7475

7576
@Test
7677
public void filesRootIsCorrectlyPassedToTransformer() {
77-
startWithExtensions(services -> new FilesUsingResponseTransformer(services.getFiles()));
78+
startWithExtensions(
79+
services -> List.of(new FilesUsingResponseTransformer(services.getFiles())));
7880

7981
wm.stubFor(get(urlEqualTo("/response-transform-with-files")).willReturn(ok()));
8082

@@ -89,7 +91,7 @@ private void startWithExtensions(Class<? extends Extension> extensionClasses) {
8991
client = new WireMockTestClient(wm.port());
9092
}
9193

92-
private void startWithExtensions(ExtensionFactory<?>... extensionFactories) {
94+
private void startWithExtensions(ExtensionFactory... extensionFactories) {
9395
wm = new WireMockServer(wireMockConfig().dynamicPort().extensions(extensionFactories));
9496
wm.start();
9597
client = new WireMockTestClient(wm.port());

src/test/java/com/github/tomakehurst/wiremock/extension/responsetemplating/helpers/HandlebarsJsonPathHelperTest.java

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.google.common.collect.ImmutableMap;
3636
import java.io.IOException;
3737
import java.util.ArrayList;
38+
import java.util.List;
3839
import java.util.Map;
3940
import org.junit.jupiter.api.BeforeEach;
4041
import org.junit.jupiter.api.Test;
@@ -274,21 +275,23 @@ public void rendersAMeaningfulErrorWhenJsonPathIsNull() {
274275
@Test
275276
public void extractsValueFromAMap() {
276277
ResponseTemplateTransformer transformer =
277-
buildExtension(
278-
new MockWireMockServices(),
279-
services ->
280-
new ResponseTemplateTransformer(
281-
services.getTemplateEngine(), true, services.getFiles()) {
282-
@Override
283-
protected Map<String, Object> addExtraModelElements(
284-
Request request,
285-
ResponseDefinition responseDefinition,
286-
FileSource files,
287-
Parameters parameters) {
288-
return ImmutableMap.<String, Object>of(
289-
"mapData", ImmutableMap.of("things", "abc"));
290-
}
291-
});
278+
(ResponseTemplateTransformer)
279+
buildExtension(
280+
new MockWireMockServices(),
281+
services ->
282+
List.of(
283+
new ResponseTemplateTransformer(
284+
services.getTemplateEngine(), true, services.getFiles()) {
285+
@Override
286+
protected Map<String, Object> addExtraModelElements(
287+
Request request,
288+
ResponseDefinition responseDefinition,
289+
FileSource files,
290+
Parameters parameters) {
291+
return ImmutableMap.<String, Object>of(
292+
"mapData", ImmutableMap.of("things", "abc"));
293+
}
294+
}));
292295

293296
final ResponseDefinition responseDefinition =
294297
transform(

src/test/java/com/github/tomakehurst/wiremock/testsupport/ExtensionFactoryUtils.java

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,44 +22,51 @@
2222
import com.github.tomakehurst.wiremock.extension.ExtensionFactory;
2323
import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer;
2424
import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformerTest;
25+
import java.util.List;
2526
import java.util.Map;
2627

2728
public class ExtensionFactoryUtils {
2829

2930
public static ResponseTemplateTransformer buildTemplateTransformer(boolean global) {
30-
return buildExtension(
31-
new MockWireMockServices(),
32-
services ->
33-
new ResponseTemplateTransformer(
34-
services.getTemplateEngine(), global, services.getFiles()));
31+
return (ResponseTemplateTransformer)
32+
buildExtension(
33+
new MockWireMockServices(),
34+
services ->
35+
List.of(
36+
new ResponseTemplateTransformer(
37+
services.getTemplateEngine(), global, services.getFiles())));
3538
}
3639

3740
public static ResponseTemplateTransformer buildTemplateTransformer(
3841
boolean global, String helperName, Helper<?> helper) {
39-
return buildExtension(
40-
new MockWireMockServices().setHelpers(Map.of(helperName, helper)),
41-
services ->
42-
new ResponseTemplateTransformer(
43-
services.getTemplateEngine(), global, services.getFiles()));
42+
return (ResponseTemplateTransformer)
43+
buildExtension(
44+
new MockWireMockServices().setHelpers(Map.of(helperName, helper)),
45+
services ->
46+
List.of(
47+
new ResponseTemplateTransformer(
48+
services.getTemplateEngine(), global, services.getFiles())));
4449
}
4550

4651
public static ResponseTemplateTransformer buildTemplateTransformer(Long maxCacheEntries) {
47-
return buildExtension(
48-
new MockWireMockServices().setMaxCacheEntries(maxCacheEntries),
49-
services ->
50-
new ResponseTemplateTransformer(
51-
services.getTemplateEngine(), false, services.getFiles()));
52+
return (ResponseTemplateTransformer)
53+
buildExtension(
54+
new MockWireMockServices().setMaxCacheEntries(maxCacheEntries),
55+
services ->
56+
List.of(
57+
new ResponseTemplateTransformer(
58+
services.getTemplateEngine(), false, services.getFiles())));
5259
}
5360

54-
public static <T extends Extension> T buildExtension(
55-
MockWireMockServices wireMockServices, ExtensionFactory<T> factory) {
61+
public static Extension buildExtension(
62+
MockWireMockServices wireMockServices, ExtensionFactory factory) {
5663
FileSource fileSource =
5764
new ClasspathFileSource(
5865
ResponseTemplateTransformerTest.class
5966
.getClassLoader()
6067
.getResource("templates")
6168
.getPath());
6269
wireMockServices.setFileSource(fileSource);
63-
return factory.create(wireMockServices);
70+
return factory.create(wireMockServices).stream().findFirst().get();
6471
}
6572
}

0 commit comments

Comments
 (0)