diff --git a/spring-cloud-contract-stub-runner/README.adoc b/spring-cloud-contract-stub-runner/README.adoc index b67974ab42..a01e329ce0 100644 --- a/spring-cloud-contract-stub-runner/README.adoc +++ b/spring-cloud-contract-stub-runner/README.adoc @@ -146,6 +146,37 @@ structure in your stubs jar. By maintaining this structure classpath gets scanned and you can profit from the messaging / HTTP stubs without the need to download artifacts. +===== Configuring HTTP Server Stubs + +Stub Runner has a notion of a `HttpServerStub` that abstracts the underlaying +concrete implementation of the HTTP server (e.g. WireMock is one of the implementations). +Sometimes, you need to perform some additional tuning of the stub servers, +that is concrete for the given implementation. To do that, Stub Runner gives you +the `httpServerStubConfigurer` property that is available in the annotation, +JUnit rule, and is accessible via system properties, where you can provide +your implementation of the `org.springframework.cloud.contract.stubrunner.HttpServerStubConfigurer` interface. The implementations can alter +the configuration files for the given HTTP server stub. + +Spring Cloud Contract Stub Runner comes with an implementation that you +can extend, for WireMock - `org.springframework.cloud.contract.stubrunner.provider.wiremock.WireMockHttpServerStubConfigurer`. In the `configure` method +you can provide your own, custom configuration for the given stub. The use +case might be starting WireMock for the given artifact id, on an HTTPs port. Example: + +.WireMockHttpServerStubConfigurer implementation +[source,groovy,indent=0] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy[tags=wireMockHttpServerStubConfigurer] +---- + +You can then reuse it via the annotation + +[source,groovy,indent=0] +---- +include::src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy[tags=annotation] +---- + +Whenever an https port is found, it will take precedence over the http one. + ==== Running stubs ===== Running using main app @@ -677,5 +708,4 @@ Then only the stubs registered under a path that contains the `foo-consumer` in `src/test/resources/contracts/foo-consumer/some/contracts/...` folder) will be allowed to be referenced. You can check out https://github.com/spring-cloud/spring-cloud-contract/issues/224[issue 224] for more -information about the reasons behind this change. - +information about the reasons behind this change. \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/HttpServerStub.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/HttpServerStub.java index 8bc2c34479..765e7aac5e 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/HttpServerStub.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/HttpServerStub.java @@ -1,3 +1,18 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.springframework.cloud.contract.stubrunner; import java.io.File; @@ -12,10 +27,17 @@ import java.util.Collection; public interface HttpServerStub { /** - * Port on which the server is running + * Port on which the server is running. Return {@code -1} if not applicable. */ int port(); + /** + * Https port on which the server is running. Return {@code -1} if not applicable. + */ + default int httpsPort() { + return -1; + } + /** * Returns {@code true} if the server is running */ @@ -23,14 +45,29 @@ public interface HttpServerStub { /** * Starts the server on a random port. Should return itself to allow chaining. + * @deprecated use {@link HttpServerStub#start(HttpServerStubConfiguration)} */ + @Deprecated HttpServerStub start(); /** * Starts the server on a given port. Should return itself to allow chaining. + * @deprecated use {@link HttpServerStub#start(HttpServerStubConfiguration)} */ + @Deprecated HttpServerStub start(int port); + /** + * Starts the server. Should return itself to allow chaining. + * @param configuration - setup for the given stub + */ + default HttpServerStub start(HttpServerStubConfiguration configuration) { + if (configuration.isRandomPort()) { + return start(); + } + return start(configuration.port); + }; + /** * Stops the server. Should return itself to allow chaining. */ diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/HttpServerStubConfiguration.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/HttpServerStubConfiguration.java new file mode 100644 index 0000000000..a30a5a6996 --- /dev/null +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/HttpServerStubConfiguration.java @@ -0,0 +1,45 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner; + +/** + * Configuration class for an {@link HttpServerStub} + * @since 2.1.0 + * @author Marcin Grzejszczak + */ +public final class HttpServerStubConfiguration { + public final HttpServerStubConfigurer configurer; + public final StubRunnerOptions stubRunnerOptions; + public final StubConfiguration stubConfiguration; + public final Integer port; + + public HttpServerStubConfiguration(HttpServerStubConfigurer configurer, StubRunnerOptions stubRunnerOptions, StubConfiguration stubConfiguration, Integer port) { + this.configurer = configurer; + this.stubRunnerOptions = stubRunnerOptions; + this.stubConfiguration = stubConfiguration; + this.port = port; + } + + public boolean isRandomPort() { + return this.port == null || this.port == 0; + } + + public String toColonSeparatedDependencyNotation() { + return this.stubConfiguration != null ? + this.stubConfiguration.toColonSeparatedDependencyNotation() : ""; + } +} \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/HttpServerStubConfigurer.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/HttpServerStubConfigurer.java new file mode 100644 index 0000000000..f5037a3a28 --- /dev/null +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/HttpServerStubConfigurer.java @@ -0,0 +1,55 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner; + +/** + * Allows to perform additional configuration of the HTTP Server stub + * + * @author Marcin Grzejszczak + * @since 2.1.0 + */ +public interface HttpServerStubConfigurer { + + /** + * Ensures that the HTTP server stub implementation configuration type is accepted + * @param httpStubConfiguration - HTTP server stub implementation + * @return {@code true} when this configurer can be applied for this object + */ + boolean isAccepted(Object httpStubConfiguration); + + /** + * Performs additional configuration of the HTTP Server Stub + * @param httpStubConfiguration - stub implementation to configure + * @param httpServerStubConfiguration - Spring Cloud Contract stub configuration + */ + default T configure(T httpStubConfiguration, + HttpServerStubConfiguration httpServerStubConfiguration) { + return httpStubConfiguration; + } + + /** + * Implementation that does nothing + */ + class NoOpHttpServerStubConfigurer implements HttpServerStubConfigurer { + public static HttpServerStubConfigurer INSTANCE = new NoOpHttpServerStubConfigurer(); + + @Override + public boolean isAccepted(Object httpStubConfiguration) { + return false; + } + } +} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerExecutor.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerExecutor.java index 46676ba955..4066bd4a40 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerExecutor.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerExecutor.java @@ -30,6 +30,8 @@ import java.util.Set; import groovy.json.JsonOutput; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import wiremock.org.eclipse.jetty.util.ConcurrentHashSet; + import org.springframework.cloud.contract.spec.Contract; import org.springframework.cloud.contract.spec.internal.DslProperty; import org.springframework.cloud.contract.spec.internal.Headers; @@ -39,7 +41,6 @@ import org.springframework.cloud.contract.stubrunner.provider.wiremock.WireMockH import org.springframework.cloud.contract.verifier.messaging.MessageVerifier; import org.springframework.cloud.contract.verifier.messaging.noop.NoOpStubMessages; import org.springframework.cloud.contract.verifier.util.BodyExtractor; -import wiremock.org.eclipse.jetty.util.ConcurrentHashSet; /** * Runs stubs for a particular {@link StubServer} @@ -72,7 +73,7 @@ class StubRunnerExecutor implements StubFinder { } StubRunnerExecutor(AvailablePortScanner portScanner) { - this(portScanner, new NoOpStubMessages(), new ArrayList()); + this(portScanner, new NoOpStubMessages(), new ArrayList<>()); } public RunningStubs runStubs(StubRunnerOptions stubRunnerOptions, @@ -84,7 +85,14 @@ class StubRunnerExecutor implements StubFinder { } return runningStubs(); } - startStubServers(stubRunnerOptions, stubConfiguration, repository); + try { + HttpServerStubConfigurer configurer = stubRunnerOptions + .getHttpServerStubConfigurer().newInstance(); + startStubServers(configurer, stubRunnerOptions, stubConfiguration, repository); + } + catch (InstantiationException | IllegalAccessException ex) { + log.error("Failed to instantiate the HTTP stub configurer", ex); + } RunningStubs runningCollaborators = runningStubs(); log.info("All stubs are now running " + runningCollaborators.toString()); return runningCollaborators; @@ -257,22 +265,26 @@ class StubRunnerExecutor implements StubFinder { return condition ? this.stubServer.getStubUrl() : null; } - private void startStubServers(final StubRunnerOptions stubRunnerOptions, + private StubServer startStubServers(HttpServerStubConfigurer configurer, + final StubRunnerOptions stubRunnerOptions, final StubConfiguration stubConfiguration, StubRepository repository) { final List mappings = repository.getStubs(); final Collection contracts = repository.contracts; Integer port = stubRunnerOptions.port(stubConfiguration); + HttpServerStubConfiguration configuration = new HttpServerStubConfiguration( + configurer, stubRunnerOptions, stubConfiguration, port + ); if (!hasRequest(contracts) && mappings.isEmpty()) { if (log.isDebugEnabled()) { log.debug("There are no HTTP related contracts. Won't start any servers"); } this.stubServer = new StubServer(stubConfiguration, mappings, contracts, - new NoOpHttpServerStub()).start(); - return; + new NoOpHttpServerStub()).start(configuration); + return this.stubServer; } if (port != null && port >= 0) { this.stubServer = new StubServer(stubConfiguration, mappings, contracts, - httpServerStub()).start(port); + httpServerStub()).start(configuration); } else { this.stubServer = this.portScanner @@ -280,11 +292,15 @@ class StubRunnerExecutor implements StubFinder { @Override public StubServer call(int availablePort) { return new StubServer(stubConfiguration, mappings, contracts, - httpServerStub()).start(availablePort); + httpServerStub()).start( + new HttpServerStubConfiguration( + configurer, stubRunnerOptions, stubConfiguration, availablePort + )); } }); } STUB_SERVERS.add(this.stubServer); + return this.stubServer; } private boolean hasRequest(Collection contracts) { diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptions.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptions.java index 2e17795b3e..fe333230d0 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptions.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptions.java @@ -26,6 +26,7 @@ import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties; import org.springframework.core.io.Resource; import org.springframework.util.StringUtils; @@ -114,13 +115,20 @@ public class StubRunnerOptions { */ private Map properties = new HashMap<>(); + /** + * Configuration for an HTTP server stub + * @return class that allows to perform additional HTTP server stub configuration + */ + private final Class httpServerStubConfigurer; + StubRunnerOptions(Integer minPortValue, Integer maxPortValue, Resource stubRepositoryRoot, StubRunnerProperties.StubsMode stubsMode, String stubsClassifier, Collection dependencies, Map stubIdsToPortMapping, String username, String password, final StubRunnerProxyOptions stubRunnerProxyOptions, boolean stubsPerConsumer, String consumerName, String mappingsOutputFolder, - boolean deleteStubsAfterTest, Map properties) { + boolean deleteStubsAfterTest, Map properties, + Class httpServerStubConfigurer) { this.minPortValue = minPortValue; this.maxPortValue = maxPortValue; this.stubRepositoryRoot = stubRepositoryRoot; @@ -137,6 +145,7 @@ public class StubRunnerOptions { this.mappingsOutputFolder = mappingsOutputFolder; this.deleteStubsAfterTest = deleteStubsAfterTest; this.properties = properties; + this.httpServerStubConfigurer = httpServerStubConfigurer; } public Integer port(StubConfiguration stubConfiguration) { @@ -253,6 +262,7 @@ public class StubRunnerOptions { return this.stubsPerConsumer; } + @Deprecated public void setStubsPerConsumer(boolean stubsPerConsumer) { this.stubsPerConsumer = stubsPerConsumer; } @@ -261,6 +271,7 @@ public class StubRunnerOptions { return this.consumerName; } + @Deprecated public void setConsumerName(String consumerName) { this.consumerName = consumerName; } @@ -273,6 +284,7 @@ public class StubRunnerOptions { return this.mappingsOutputFolder; } + @Deprecated public void setMappingsOutputFolder(String mappingsOutputFolder) { this.mappingsOutputFolder = mappingsOutputFolder; } @@ -281,6 +293,7 @@ public class StubRunnerOptions { return this.deleteStubsAfterTest; } + @Deprecated public void setDeleteStubsAfterTest(boolean deleteStubsAfterTest) { this.deleteStubsAfterTest = deleteStubsAfterTest; } @@ -289,10 +302,15 @@ public class StubRunnerOptions { return this.properties; } + @Deprecated public void setProperties(Map properties) { this.properties = properties; } + public Class getHttpServerStubConfigurer() { + return this.httpServerStubConfigurer; + } + public static class StubRunnerProxyOptions { private final String proxyHost; @@ -317,7 +335,6 @@ public class StubRunnerOptions { return "StubRunnerProxyOptions{" + "proxyHost='" + this.proxyHost + '\'' + ", proxyPort=" + this.proxyPort + '}'; } - } @Override @@ -331,7 +348,7 @@ public class StubRunnerOptions { + '\'' + ", password='" + obfuscate(this.password) + '\'' + ", stubRunnerProxyOptions='" + this.stubRunnerProxyOptions + "', stubsPerConsumer='" + this.stubsPerConsumer + '\'' - + ", stubsPerConsumer='" + this.stubsPerConsumer + '\'' + '}'; + + ", httpServerStubConfigurer='" + this.httpServerStubConfigurer+ '\'' + '}'; } private String obfuscate(String string) { diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilder.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilder.java index a160d07f2b..dce659b230 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilder.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilder.java @@ -66,6 +66,10 @@ public class StubRunnerOptionsBuilder { private Map properties = new HashMap<>(); + private Class httpServerStubConfigurer = + HttpServerStubConfigurer.NoOpHttpServerStubConfigurer.class; + + public StubRunnerOptionsBuilder() { } @@ -174,13 +178,18 @@ public class StubRunnerOptionsBuilder { return this; } + public StubRunnerOptionsBuilder withHttpServerStubConfigurer(Class httpServerStubConfigurer) { + this.httpServerStubConfigurer = httpServerStubConfigurer; + return this; + } + public StubRunnerOptions build() { return new StubRunnerOptions(this.minPortValue, this.maxPortValue, this.stubRepositoryRoot, this.stubsMode, this.stubsClassifier, buildDependencies(), this.stubIdsToPortMapping, this.username, this.password, this.stubRunnerProxyOptions, this.stubsPerConsumer, this.consumerName, this.mappingsOutputFolder, this.deleteStubsAfterTest, - this.properties); + this.properties, this.httpServerStubConfigurer); } private Collection buildDependencies() { diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubServer.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubServer.java index 76d543d730..d252488dcb 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubServer.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubServer.java @@ -46,13 +46,8 @@ class StubServer { this.contracts = contracts; } - public StubServer start() { - this.httpServerStub.start(); - return stubServer(); - } - - public StubServer start(int port) { - this.httpServerStub.start(port); + public StubServer start(HttpServerStubConfiguration configuration) { + this.httpServerStub.start(configuration); return stubServer(); } @@ -77,7 +72,12 @@ class StubServer { public int getPort() { if (this.httpServerStub.isRunning()) { - return this.httpServerStub.port(); + int httpsPort = this.httpServerStub.httpsPort(); + int httpPort = this.httpServerStub.port(); + if (log.isDebugEnabled()) { + log.debug("Ports for this server are https [" + httpsPort + "] and http [" + httpPort + "]"); + } + return httpsPort != -1 ? httpsPort : httpPort; } if (log.isDebugEnabled()) { log.debug("The HTTP Server stub is not running... That means that the " @@ -86,9 +86,14 @@ class StubServer { return -1; } + private boolean hasHttps() { + int httpsPort = this.httpServerStub.httpsPort(); + return httpsPort != -1; + } + public URL getStubUrl() { try { - return new URL("http://localhost:" + getPort()); + return new URL((hasHttps() ? "https:" : "http:") + "//localhost:" + getPort()); } catch (MalformedURLException e) { throw new IllegalStateException("Cannot parse URL", e); @@ -107,6 +112,10 @@ class StubServer { return this.httpServerStub.registeredMappings(); } + HttpServerStub httpServerStub() { + return this.httpServerStub; + } + @Override public boolean equals(Object o) { if (this == o) diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerExtension.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerExtension.java index 0773d10e42..a8f9ad9478 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerExtension.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerExtension.java @@ -16,14 +16,23 @@ package org.springframework.cloud.contract.stubrunner.junit; +import java.io.IOException; +import java.net.URL; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.ExtensionContext; + import org.springframework.cloud.contract.spec.Contract; import org.springframework.cloud.contract.stubrunner.BatchStubRunner; import org.springframework.cloud.contract.stubrunner.BatchStubRunnerFactory; +import org.springframework.cloud.contract.stubrunner.HttpServerStubConfigurer; import org.springframework.cloud.contract.stubrunner.RunningStubs; import org.springframework.cloud.contract.stubrunner.StubConfiguration; import org.springframework.cloud.contract.stubrunner.StubFinder; @@ -33,13 +42,6 @@ import org.springframework.cloud.contract.stubrunner.StubRunnerOptionsBuilder; import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties; import org.springframework.cloud.contract.verifier.messaging.MessageVerifier; -import java.io.IOException; -import java.net.URL; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; - /** * JUnit 5 extension that allows to download and run stubs. * @@ -48,237 +50,245 @@ import java.util.Map; */ public class StubRunnerExtension implements BeforeAllCallback, AfterAllCallback, StubFinder, StubRunnerExtensionOptions { - private static final String DELIMITER = ":"; - private static final String LATEST_VERSION = "+"; - private static final Log LOG = LogFactory.getLog(StubRunnerExtension.class); + private static final String DELIMITER = ":"; + private static final String LATEST_VERSION = "+"; + private static final Log LOG = LogFactory.getLog(StubRunnerExtension.class); - StubRunnerExtension delegate = this; - private BatchStubRunner stubFinder; - private StubRunnerOptionsBuilder stubRunnerOptionsBuilder = new StubRunnerOptionsBuilder( - StubRunnerOptions.fromSystemProps()); - private MessageVerifier verifier = new ExceptionThrowingMessageVerifier(); + StubRunnerExtension delegate = this; + private BatchStubRunner stubFinder; + private StubRunnerOptionsBuilder stubRunnerOptionsBuilder = new StubRunnerOptionsBuilder( + StubRunnerOptions.fromSystemProps()); + private MessageVerifier verifier = new ExceptionThrowingMessageVerifier(); - public StubRunnerExtension() { - } + public StubRunnerExtension() { + } - StubRunnerExtension(StubRunnerExtension delegate) { - this.delegate = delegate; - } + StubRunnerExtension(StubRunnerExtension delegate) { + this.delegate = delegate; + } - @Override - public void beforeAll(ExtensionContext extensionContext) { - stubFinder(new BatchStubRunnerFactory(builder().build(), verifier()).buildBatchStubRunner()); - stubFinder().runStubs(); - } + @Override + public void beforeAll(ExtensionContext extensionContext) { + stubFinder(new BatchStubRunnerFactory(builder().build(), verifier()) + .buildBatchStubRunner()); + stubFinder().runStubs(); + } - @Override - public void afterAll(ExtensionContext extensionContext) { - try { - stubFinder().close(); - } catch (IOException exception) { - LOG.warn(exception.getMessage(), exception); - } - } + @Override + public void afterAll(ExtensionContext extensionContext) { + try { + stubFinder().close(); + } + catch (IOException exception) { + LOG.warn(exception.getMessage(), exception); + } + } - @Override - public URL findStubUrl(String groupId, String artifactId) throws StubNotFoundException { - return stubFinder().findStubUrl(groupId, artifactId); - } + @Override + public URL findStubUrl(String groupId, String artifactId) throws StubNotFoundException { + return stubFinder().findStubUrl(groupId, artifactId); + } - @Override - public URL findStubUrl(String ivyNotation) throws StubNotFoundException { - return stubFinder().findStubUrl(ivyNotation); - } + @Override + public URL findStubUrl(String ivyNotation) throws StubNotFoundException { + return stubFinder().findStubUrl(ivyNotation); + } - @Override - public RunningStubs findAllRunningStubs() { - return stubFinder().findAllRunningStubs(); - } + @Override + public RunningStubs findAllRunningStubs() { + return stubFinder().findAllRunningStubs(); + } - @Override - public Map> getContracts() { - return stubFinder().getContracts(); - } + @Override + public Map> getContracts() { + return stubFinder().getContracts(); + } - @Override - public boolean trigger(String ivyNotation, String labelName) { - boolean result = stubFinder().trigger(ivyNotation, labelName); - if (!result) { - throw new IllegalStateException("Failed to trigger a message with notation [" - + ivyNotation + "] and label [" + labelName + "]"); - } - return result; - } + @Override + public boolean trigger(String ivyNotation, String labelName) { + boolean result = stubFinder().trigger(ivyNotation, labelName); + if (!result) { + throw new IllegalStateException("Failed to trigger a message with notation [" + + ivyNotation + "] and label [" + labelName + "]"); + } + return result; + } - @Override - public boolean trigger(String labelName) { - boolean result = stubFinder().trigger(labelName); - if (!result) { - throw new IllegalStateException( - "Failed to trigger a message with label [" + labelName + "]"); - } - return result; - } + @Override + public boolean trigger(String labelName) { + boolean result = stubFinder().trigger(labelName); + if (!result) { + throw new IllegalStateException( + "Failed to trigger a message with label [" + labelName + "]"); + } + return result; + } - @Override - public boolean trigger() { - boolean result = stubFinder().trigger(); - if (!result) { - throw new IllegalStateException("Failed to trigger a message"); - } - return result; - } + @Override + public boolean trigger() { + boolean result = stubFinder().trigger(); + if (!result) { + throw new IllegalStateException("Failed to trigger a message"); + } + return result; + } - @Override - public Map> labels() { - return stubFinder().labels(); - } + @Override + public Map> labels() { + return stubFinder().labels(); + } - @Override - public StubRunnerExtension messageVerifier(MessageVerifier messageVerifier) { - verifier(messageVerifier); - return delegate; - } + @Override + public StubRunnerExtension messageVerifier(MessageVerifier messageVerifier) { + verifier(messageVerifier); + return this.delegate; + } - @Override - public StubRunnerExtension options(StubRunnerOptions stubRunnerOptions) { - builder().withOptions(stubRunnerOptions); - return delegate; - } + @Override + public StubRunnerExtension options(StubRunnerOptions stubRunnerOptions) { + builder().withOptions(stubRunnerOptions); + return this.delegate; + } - @Override - public StubRunnerExtension minPort(int minPort) { - builder().withMinPort(minPort); - return delegate; - } + @Override + public StubRunnerExtension minPort(int minPort) { + builder().withMinPort(minPort); + return this.delegate; + } - @Override - public StubRunnerExtension maxPort(int maxPort) { - builder().withMaxPort(maxPort); - return delegate; - } + @Override + public StubRunnerExtension maxPort(int maxPort) { + builder().withMaxPort(maxPort); + return this.delegate; + } - @Override - public StubRunnerExtension repoRoot(String repoRoot) { - builder().withStubRepositoryRoot(repoRoot); - return delegate; - } + @Override + public StubRunnerExtension repoRoot(String repoRoot) { + builder().withStubRepositoryRoot(repoRoot); + return this.delegate; + } - @Override - public StubRunnerExtension stubsMode(StubRunnerProperties.StubsMode stubsMode) { - builder().withStubsMode(stubsMode); - return delegate; - } + @Override + public StubRunnerExtension stubsMode(StubRunnerProperties.StubsMode stubsMode) { + builder().withStubsMode(stubsMode); + return this.delegate; + } - @Override - public PortStubRunnerExtension downloadStub(String groupId, String artifactId, String version, String classifier) { - builder().withStubs(groupId + DELIMITER + artifactId + DELIMITER + version - + DELIMITER + classifier); - return new PortStubRunnerExtension(delegate); - } + @Override + public PortStubRunnerExtension downloadStub(String groupId, String artifactId, String version, String classifier) { + builder().withStubs(groupId + DELIMITER + artifactId + DELIMITER + version + + DELIMITER + classifier); + return new PortStubRunnerExtension(this.delegate); + } - @Override - public PortStubRunnerExtension downloadLatestStub(String groupId, String artifactId, String classifier) { - builder().withStubs(groupId + DELIMITER + artifactId + DELIMITER + LATEST_VERSION - + DELIMITER + classifier); - return new PortStubRunnerExtension(delegate); - } + @Override + public PortStubRunnerExtension downloadLatestStub(String groupId, String artifactId, String classifier) { + builder().withStubs(groupId + DELIMITER + artifactId + DELIMITER + LATEST_VERSION + + DELIMITER + classifier); + return new PortStubRunnerExtension(this.delegate); + } - @Override - public PortStubRunnerExtension downloadStub(String groupId, String artifactId, String version) { - builder().withStubs(groupId + DELIMITER + artifactId + DELIMITER + version); - return new PortStubRunnerExtension(delegate); - } + @Override + public PortStubRunnerExtension downloadStub(String groupId, String artifactId, String version) { + builder().withStubs(groupId + DELIMITER + artifactId + DELIMITER + version); + return new PortStubRunnerExtension(this.delegate); + } - @Override - public PortStubRunnerExtension downloadStub(String groupId, String artifactId) { - builder().withStubs(groupId + DELIMITER + artifactId); - return new PortStubRunnerExtension(delegate); - } + @Override + public PortStubRunnerExtension downloadStub(String groupId, String artifactId) { + builder().withStubs(groupId + DELIMITER + artifactId); + return new PortStubRunnerExtension(this.delegate); + } - @Override - public PortStubRunnerExtension downloadStub(String ivyNotation) { - builder().withStubs(ivyNotation); - return new PortStubRunnerExtension(delegate); - } + @Override + public PortStubRunnerExtension downloadStub(String ivyNotation) { + builder().withStubs(ivyNotation); + return new PortStubRunnerExtension(this.delegate); + } - @Override - public StubRunnerExtension downloadStubs(String... ivyNotations) { - builder().withStubs(Arrays.asList(ivyNotations)); - return new PortStubRunnerExtension(delegate); - } + @Override + public StubRunnerExtension downloadStubs(String... ivyNotations) { + builder().withStubs(Arrays.asList(ivyNotations)); + return new PortStubRunnerExtension(this.delegate); + } - @Override - public StubRunnerExtension downloadStubs(List ivyNotations) { - builder().withStubs(ivyNotations); - return new PortStubRunnerExtension(delegate); - } + @Override + public StubRunnerExtension downloadStubs(List ivyNotations) { + builder().withStubs(ivyNotations); + return new PortStubRunnerExtension(this.delegate); + } - @Override - public StubRunnerExtension withStubPerConsumer(boolean stubPerConsumer) { - builder().withStubPerConsumer(stubPerConsumer); - return new PortStubRunnerExtension(delegate); - } + @Override + public StubRunnerExtension withStubPerConsumer(boolean stubPerConsumer) { + builder().withStubPerConsumer(stubPerConsumer); + return new PortStubRunnerExtension(this.delegate); + } - @Override - public StubRunnerExtension withConsumerName(String consumerName) { - builder().withConsumerName(consumerName); - return new PortStubRunnerExtension(delegate); - } + @Override + public StubRunnerExtension withConsumerName(String consumerName) { + builder().withConsumerName(consumerName); + return new PortStubRunnerExtension(this.delegate); + } - @Override - public StubRunnerExtension withMappingsOutputFolder(String mappingsOutputFolder) { - builder().withMappingsOutputFolder(mappingsOutputFolder); - return new PortStubRunnerExtension(delegate); - } + @Override + public StubRunnerExtension withMappingsOutputFolder(String mappingsOutputFolder) { + builder().withMappingsOutputFolder(mappingsOutputFolder); + return new PortStubRunnerExtension(this.delegate); + } - @Override - public StubRunnerExtension withDeleteStubsAfterTest(boolean deleteStubsAfterTest) { - builder().withDeleteStubsAfterTest(deleteStubsAfterTest); - return new PortStubRunnerExtension(delegate); - } + @Override + public StubRunnerExtension withDeleteStubsAfterTest(boolean deleteStubsAfterTest) { + builder().withDeleteStubsAfterTest(deleteStubsAfterTest); + return new PortStubRunnerExtension(this.delegate); + } - @Override - public StubRunnerExtension withProperties(Map properties) { - builder().withProperties(properties); - return new PortStubRunnerExtension(delegate); - } + @Override + public StubRunnerExtension withProperties(Map properties) { + builder().withProperties(properties); + return new PortStubRunnerExtension(this.delegate); + } - BatchStubRunner stubFinder() { - return this.delegate.stubFinder; - } + @Override + public StubRunnerExtension withHttpServerStubConfigurer(Class httpServerStubConfigurer) { + builder().withHttpServerStubConfigurer(httpServerStubConfigurer); + return new PortStubRunnerExtension(this.delegate); + } - void stubFinder(BatchStubRunner stubFinder) { - this.delegate.stubFinder = stubFinder; - } + BatchStubRunner stubFinder() { + return this.delegate.stubFinder; + } - StubRunnerOptionsBuilder builder() { - return delegate.stubRunnerOptionsBuilder; - } + void stubFinder(BatchStubRunner stubFinder) { + this.delegate.stubFinder = stubFinder; + } - MessageVerifier verifier() { - return delegate.verifier; - } + StubRunnerOptionsBuilder builder() { + return this.delegate.stubRunnerOptionsBuilder; + } - void verifier(MessageVerifier verifier) { - delegate.verifier = verifier; - } + MessageVerifier verifier() { + return this.delegate.verifier; + } - /** - * Helper class with additional port, related methods once you pick a stub to download - * - * @since 1.2.0 - */ - public static class PortStubRunnerExtension extends StubRunnerExtension implements PortStubRunnerExtensionOptions { + void verifier(MessageVerifier verifier) { + this.delegate.verifier = verifier; + } - PortStubRunnerExtension(StubRunnerExtension delegate) { - super(delegate); - } + /** + * Helper class with additional port, related methods once you pick a stub to download + * + * @since 1.2.0 + */ + public static class PortStubRunnerExtension extends StubRunnerExtension implements PortStubRunnerExtensionOptions { - @Override - public StubRunnerExtension withPort(Integer port) { - builder().withPort(port); - return delegate; - } - } + PortStubRunnerExtension(StubRunnerExtension delegate) { + super(delegate); + } + + @Override + public StubRunnerExtension withPort(Integer port) { + builder().withPort(port); + return this.delegate; + } + } } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerExtensionOptions.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerExtensionOptions.java index a9ca98d922..6d35a46682 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerExtensionOptions.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerExtensionOptions.java @@ -16,119 +16,125 @@ package org.springframework.cloud.contract.stubrunner.junit; +import java.util.List; +import java.util.Map; + +import org.springframework.cloud.contract.stubrunner.HttpServerStubConfigurer; import org.springframework.cloud.contract.stubrunner.StubRunnerOptions; import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties; import org.springframework.cloud.contract.verifier.messaging.MessageVerifier; -import java.util.List; -import java.util.Map; - /** * @author Olga Maciaszek-Sharma * @since 2.1.0 */ interface StubRunnerExtensionOptions { - /** - * Pass the {@link MessageVerifier} that this rule should use. If you don't pass - * anything a {@link ExceptionThrowingMessageVerifier} will be used. - * That means that an exception will be thrown whenever you try to do sth messaging - * related. - */ - StubRunnerExtension messageVerifier(MessageVerifier messageVerifier); + /** + * Pass the {@link MessageVerifier} that this rule should use. If you don't pass + * anything a {@link ExceptionThrowingMessageVerifier} will be used. + * That means that an exception will be thrown whenever you try to do sth messaging + * related. + */ + StubRunnerExtension messageVerifier(MessageVerifier messageVerifier); - /** - * Override all options - * - * @see StubRunnerOptions - */ - StubRunnerExtension options(StubRunnerOptions stubRunnerOptions); + /** + * Override all options + * + * @see StubRunnerOptions + */ + StubRunnerExtension options(StubRunnerOptions stubRunnerOptions); - /** - * Min value of port for WireMock server - */ - StubRunnerExtension minPort(int minPort); + /** + * Min value of port for WireMock server + */ + StubRunnerExtension minPort(int minPort); - /** - * Max value of port for WireMock server - */ - StubRunnerExtension maxPort(int maxPort); + /** + * Max value of port for WireMock server + */ + StubRunnerExtension maxPort(int maxPort); - /** - * String URI of repository containing stubs - */ - StubRunnerExtension repoRoot(String repoRoot); + /** + * String URI of repository containing stubs + */ + StubRunnerExtension repoRoot(String repoRoot); - /** - * Stubs mode that should be used - */ - StubRunnerExtension stubsMode(StubRunnerProperties.StubsMode stubsMode); + /** + * Stubs mode that should be used + */ + StubRunnerExtension stubsMode(StubRunnerProperties.StubsMode stubsMode); - /** - * Group Id, artifact Id, version and classifier of a single stub to download - */ - PortStubRunnerExtensionOptions downloadStub(String groupId, String artifactId, - String version, String classifier); + /** + * Group Id, artifact Id, version and classifier of a single stub to download + */ + PortStubRunnerExtensionOptions downloadStub(String groupId, String artifactId, + String version, String classifier); - /** - * Group Id, artifact Id and classifier of a single stub to download in the latest - * version - */ - PortStubRunnerExtensionOptions downloadLatestStub(String groupId, String artifactId, - String classifier); + /** + * Group Id, artifact Id and classifier of a single stub to download in the latest + * version + */ + PortStubRunnerExtensionOptions downloadLatestStub(String groupId, String artifactId, + String classifier); - /** - * Group Id, artifact Id and version of a single stub to download - */ - PortStubRunnerExtensionOptions downloadStub(String groupId, String artifactId, - String version); + /** + * Group Id, artifact Id and version of a single stub to download + */ + PortStubRunnerExtensionOptions downloadStub(String groupId, String artifactId, + String version); - /** - * Group Id, artifact Id of a single stub to download. Default classifier will be - * picked. - */ - PortStubRunnerExtensionOptions downloadStub(String groupId, String artifactId); + /** + * Group Id, artifact Id of a single stub to download. Default classifier will be + * picked. + */ + PortStubRunnerExtensionOptions downloadStub(String groupId, String artifactId); - /** - * Ivy notation of a single stub to download. - */ - PortStubRunnerExtensionOptions downloadStub(String ivyNotation); + /** + * Ivy notation of a single stub to download. + */ + PortStubRunnerExtensionOptions downloadStub(String ivyNotation); - /** - * Stubs to download in Ivy notations - */ - StubRunnerExtension downloadStubs(String... ivyNotations); + /** + * Stubs to download in Ivy notations + */ + StubRunnerExtension downloadStubs(String... ivyNotations); - /** - * Stubs to download in Ivy notations - */ - StubRunnerExtension downloadStubs(List ivyNotations); + /** + * Stubs to download in Ivy notations + */ + StubRunnerExtension downloadStubs(List ivyNotations); - /** - * Allows stub per consumer - */ - StubRunnerExtension withStubPerConsumer(boolean stubPerConsumer); + /** + * Allows stub per consumer + */ + StubRunnerExtension withStubPerConsumer(boolean stubPerConsumer); - /** - * Allows setting consumer name - */ - StubRunnerExtension withConsumerName(String consumerName); + /** + * Allows setting consumer name + */ + StubRunnerExtension withConsumerName(String consumerName); - /** - * Allows setting the output folder for mappings - */ - StubRunnerExtension withMappingsOutputFolder(String mappingsOutputFolder); + /** + * Allows setting the output folder for mappings + */ + StubRunnerExtension withMappingsOutputFolder(String mappingsOutputFolder); - /** - * If set to {@code false} will NOT delete stubs from a temporary folder after running - * tests - */ - StubRunnerExtension withDeleteStubsAfterTest(boolean deleteStubsAfterTest); + /** + * If set to {@code false} will NOT delete stubs from a temporary folder after running + * tests + */ + StubRunnerExtension withDeleteStubsAfterTest(boolean deleteStubsAfterTest); - /** - * Map of properties that can be passed to custom - * {@link org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder} - */ - StubRunnerExtension withProperties(Map properties); + /** + * Map of properties that can be passed to custom + * {@link org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder} + */ + StubRunnerExtension withProperties(Map properties); + + /** + * Configuration for an HTTP server stub + */ + StubRunnerExtension withHttpServerStubConfigurer(Class httpServerStubConfigurer); } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRule.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRule.java index 85a37ed300..156b46ebd6 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRule.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRule.java @@ -16,12 +16,20 @@ package org.springframework.cloud.contract.stubrunner.junit; +import java.net.URL; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; + import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; + import org.springframework.cloud.contract.spec.Contract; import org.springframework.cloud.contract.stubrunner.BatchStubRunner; import org.springframework.cloud.contract.stubrunner.BatchStubRunnerFactory; +import org.springframework.cloud.contract.stubrunner.HttpServerStubConfigurer; import org.springframework.cloud.contract.stubrunner.RunningStubs; import org.springframework.cloud.contract.stubrunner.StubConfiguration; import org.springframework.cloud.contract.stubrunner.StubFinder; @@ -30,12 +38,6 @@ import org.springframework.cloud.contract.stubrunner.StubRunnerOptionsBuilder; import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties; import org.springframework.cloud.contract.verifier.messaging.MessageVerifier; -import java.net.URL; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; - /** * JUnit class rule that allows you to download the provided stubs. * @@ -190,6 +192,12 @@ public class StubRunnerRule implements TestRule, StubFinder, StubRunnerRuleOptio return this.delegate; } + @Override + public StubRunnerRule withHttpServerStubConfigurer(Class httpServerStubConfigurer) { + builder().withHttpServerStubConfigurer(httpServerStubConfigurer); + return this.delegate; + } + @Override public URL findStubUrl(String groupId, String artifactId) { return this.stubFinder().findStubUrl(groupId, artifactId); diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleOptions.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleOptions.java index 3eeda394e6..870b6f5082 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleOptions.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleOptions.java @@ -16,13 +16,14 @@ package org.springframework.cloud.contract.stubrunner.junit; +import java.util.List; +import java.util.Map; + +import org.springframework.cloud.contract.stubrunner.HttpServerStubConfigurer; import org.springframework.cloud.contract.stubrunner.StubRunnerOptions; import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties; import org.springframework.cloud.contract.verifier.messaging.MessageVerifier; -import java.util.List; -import java.util.Map; - interface StubRunnerRuleOptions { /** @@ -127,4 +128,9 @@ interface StubRunnerRuleOptions { */ StubRunnerRule withProperties(Map properties); + /** + * Configuration for an HTTP server stub + */ + StubRunnerRule withHttpServerStubConfigurer(Class httpServerStubConfigurer); + } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStub.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStub.java index fbee55382d..bbec5d33af 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStub.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStub.java @@ -34,12 +34,16 @@ import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.common.Slf4jNotifier; import com.github.tomakehurst.wiremock.core.WireMockConfiguration; import com.github.tomakehurst.wiremock.extension.Extension; +import com.github.tomakehurst.wiremock.security.ClientAuthenticator; +import com.github.tomakehurst.wiremock.security.NoClientAuthenticator; import com.github.tomakehurst.wiremock.stubbing.StubMapping; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import wiremock.com.github.jknack.handlebars.Helper; import org.springframework.cloud.contract.stubrunner.HttpServerStub; +import org.springframework.cloud.contract.stubrunner.HttpServerStubConfiguration; +import org.springframework.cloud.contract.stubrunner.HttpServerStubConfigurer; import org.springframework.cloud.contract.verifier.builder.handlebars.HandlebarsEscapeHelper; import org.springframework.cloud.contract.verifier.builder.handlebars.HandlebarsJsonPathHelper; import org.springframework.cloud.contract.verifier.dsl.wiremock.DefaultResponseTransformer; @@ -67,6 +71,10 @@ public class WireMockHttpServerStub implements HttpServerStub { private WireMockServer wireMockServer; + private boolean https = false; + + private WireMockConfiguration wireMockConfiguration; + private WireMockConfiguration config() { if (ClassUtils.isPresent( "org.springframework.cloud.contract.wiremock.WireMockSpring", null)) { @@ -105,7 +113,9 @@ public class WireMockHttpServerStub implements HttpServerStub { @Override public int port() { - return isRunning() ? this.wireMockServer.port() : INVALID_PORT; + return isRunning() ? (this.https ? + this.wireMockServer.httpsPort() : this.wireMockServer.port()) + : INVALID_PORT; } @Override @@ -122,23 +132,55 @@ public class WireMockHttpServerStub implements HttpServerStub { return this; } int port = SocketUtils.findAvailableTcpPort(); - HttpServerStub serverStub = start(port); + HttpServerStub serverStub = start(defaultConfiguration(port)); cacheStubServer(true, port); return serverStub; } + private HttpServerStubConfiguration defaultConfiguration(int port) { + return new HttpServerStubConfiguration(HttpServerStubConfigurer.NoOpHttpServerStubConfigurer.INSTANCE, null, null, port); + } + @Override public HttpServerStub start(int port) { - this.wireMockServer = new WireMockServer( - config().port(port).notifier(new Slf4jNotifier(true))); + return start(defaultConfiguration(port)); + } + + @Override + public HttpServerStub start( + HttpServerStubConfiguration configuration) { + if (isRunning()) { + if (log.isTraceEnabled()) { + log.trace("The server is already running at port [" + port() + "]"); + } + return this; + } + int port = configuration.port; + WireMockConfiguration wireMockConfiguration = config().port(port) + .notifier(new Slf4jNotifier(true)); + if (configuration.configurer.isAccepted(wireMockConfiguration)) { + @SuppressWarnings("unchecked") + HttpServerStubConfigurer configurer = configuration.configurer; + wireMockConfiguration = configurer.configure(wireMockConfiguration, configuration); + } + this.wireMockConfiguration = wireMockConfiguration; + this.https = wireMockConfiguration.httpsSettings().enabled(); + port = this.https ? wireMockConfiguration.httpsSettings().port() : + wireMockConfiguration.portNumber(); + this.wireMockServer = new WireMockServer(wireMockConfiguration); this.wireMockServer.start(); if (log.isDebugEnabled()) { - log.debug("Started WireMock at port [" + port + "]"); + log.debug("For " + configuration.toColonSeparatedDependencyNotation() + " Started WireMock at [" + (this.https ? "https" : "http") + "] port [" + port + "]"); } cacheStubServer(false, port); return this; } + @Override + public int httpsPort() { + return this.https ? port() : INVALID_PORT; + } + @Override public HttpServerStub reset() { this.wireMockServer.resetAll(); @@ -205,7 +247,16 @@ public class WireMockHttpServerStub implements HttpServerStub { } private WireMock wireMock() { - return new WireMock("localhost", port(), ""); + String scheme = this.https ? "https" : "http"; + String host = "localhost"; + int port = port(); + String urlPathPrefix = null; + String hostHeader = null; + String proxyHost = this.wireMockConfiguration.proxyHostHeader(); + int proxyPort = this.wireMockConfiguration.proxyVia().port(); + ClientAuthenticator authenticator = NoClientAuthenticator.noClientAuthenticator(); + return new WireMock(scheme, host, port, urlPathPrefix, + hostHeader, proxyHost, proxyPort, authenticator); } private void registerDefaultHealthChecks(WireMock wireMock) { @@ -240,16 +291,6 @@ public class WireMockHttpServerStub implements HttpServerStub { return mapping; } - void registerDescriptors(List stubMappings) { - if (log.isDebugEnabled()) { - log.debug("Registering stub mappings size [" + stubMappings.size() - + "] at port [" + port() + "]"); - } - for (StubMapping mapping : stubMappings) { - wireMock().register(mapping); - } - } - private void registerHealthCheck(WireMock wireMock, String url) { registerHealthCheck(wireMock, url, "OK"); } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStubConfigurer.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStubConfigurer.java new file mode 100644 index 0000000000..fbac9f4690 --- /dev/null +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStubConfigurer.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.stubrunner.provider.wiremock; + +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; + +import org.springframework.cloud.contract.stubrunner.HttpServerStubConfigurer; + +/** + * Typed implementation of {@link HttpServerStubConfigurer} for WireMock's {@link WireMockConfiguration} + * + * @author Marcin Grzejszczak + * @since 2.1.0 + */ +public class WireMockHttpServerStubConfigurer implements HttpServerStubConfigurer { + @Override + public boolean isAccepted(Object httpStubConfiguration) { + return httpStubConfiguration instanceof WireMockConfiguration; + } +} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/AutoConfigureStubRunner.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/AutoConfigureStubRunner.java index 59fba2fa4d..bf8fd75307 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/AutoConfigureStubRunner.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/AutoConfigureStubRunner.java @@ -25,6 +25,7 @@ import java.lang.annotation.Target; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.test.autoconfigure.properties.PropertyMapping; import org.springframework.boot.test.autoconfigure.properties.SkipPropertyMapping; +import org.springframework.cloud.contract.stubrunner.HttpServerStubConfigurer; import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier; /** @@ -132,4 +133,11 @@ public @interface AutoConfigureStubRunner { * folder after running tests */ boolean deleteStubsAfterTest() default true; + + /** + * Configuration for an HTTP server stub + * @return class that allows to perform additional HTTP server stub configuration + */ + Class httpServerStubConfigurer() + default HttpServerStubConfigurer.NoOpHttpServerStubConfigurer.class; } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfiguration.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfiguration.java index 92923e749d..b13f78c09b 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfiguration.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfiguration.java @@ -97,7 +97,8 @@ public class StubRunnerConfiguration { .withConsumerName(consumerName()) .withMappingsOutputFolder(this.props.getMappingsOutputFolder()) .withDeleteStubsAfterTest(this.props.isDeleteStubsAfterTest()) - .withProperties(this.props.getProperties()); + .withProperties(this.props.getProperties()) + .withHttpServerStubConfigurer(this.props.getHttpServerStubConfigurer()); } private String consumerName() { diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerProperties.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerProperties.java index 3cc6dc3034..c9dd300058 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerProperties.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerProperties.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.Properties; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.contract.stubrunner.HttpServerStubConfigurer; import org.springframework.cloud.contract.stubrunner.ResourceResolver; import org.springframework.core.io.Resource; import org.springframework.util.StringUtils; @@ -113,6 +114,12 @@ public class StubRunnerProperties { */ private Map properties = new HashMap<>(); + /** + * Configuration for an HTTP server stub + */ + private Class httpServerStubConfigurer = + HttpServerStubConfigurer.NoOpHttpServerStubConfigurer.class; + /** * An enumeration stub modes. */ @@ -251,6 +258,14 @@ public class StubRunnerProperties { return this.properties; } + public Class getHttpServerStubConfigurer() { + return this.httpServerStubConfigurer; + } + + public void setHttpServerStubConfigurer(Class httpServerStubConfigurer) { + this.httpServerStubConfigurer = httpServerStubConfigurer; + } + public void setProperties(String[] properties) { Properties elements = StringUtils.splitArrayElementsIntoProperties(properties, "="); diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilderSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilderSpec.groovy index a50c85e8e7..71a4c5d4c6 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilderSpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilderSpec.groovy @@ -231,7 +231,7 @@ class StubRunnerOptionsBuilderSpec extends Specification { given: StubRunnerOptionsBuilder builder = builder.withOptions(new StubRunnerOptions(1, 2, new FileSystemResource("root"), StubRunnerProperties.StubsMode.LOCAL, "classifier", [new StubConfiguration("a:b:c")], [(new StubConfiguration("a:b:c")): 3], "foo", "bar", - new StubRunnerOptions.StubRunnerProxyOptions("host", 4), true, "consumer", "folder", false, [foo: "bar"])) + new StubRunnerOptions.StubRunnerProxyOptions("host", 4), true, "consumer", "folder", false, [foo: "bar"], Foo)) builder.withStubs("foo:bar:baz") when: StubRunnerOptions options = builder.build() @@ -252,6 +252,7 @@ class StubRunnerOptionsBuilderSpec extends Specification { options.mappingsOutputFolder == "folder" options.deleteStubsAfterTest == false options.properties == [foo: "bar"] + options.httpServerStubConfigurer == Foo } def shouldNotPrintUsernameAndPassword() { @@ -259,7 +260,7 @@ class StubRunnerOptionsBuilderSpec extends Specification { StubRunnerOptionsBuilder builder = builder.withOptions(new StubRunnerOptions(1, 2, new FileSystemResource("root"), StubRunnerProperties.StubsMode.CLASSPATH, "classifier", [new StubConfiguration("a:b:c")], [(new StubConfiguration("a:b:c")): 3], "username123", "password123", - new StubRunnerOptions.StubRunnerProxyOptions("host", 4), true, "consumer", "folder", false, [:])) + new StubRunnerOptions.StubRunnerProxyOptions("host", 4), true, "consumer", "folder", false, [:], Foo)) builder.withStubs("foo:bar:baz") when: String options = builder.build().toString() @@ -289,6 +290,7 @@ class StubRunnerOptionsBuilderSpec extends Specification { System.setProperty("stubrunner.properties.foo-bar", "bar") System.setProperty("stubrunner.properties.foo-baz", "baz") System.setProperty("stubrunner.properties.bar.bar", "foo") + System.setProperty("stubrunner.httpServerStubConfigurer", "org.springframework.cloud.contract.stubrunner.Foo") when: StubRunnerOptions options = StubRunnerOptions.fromSystemProps() then: @@ -306,5 +308,14 @@ class StubRunnerOptionsBuilderSpec extends Specification { options.consumerName == "consumer" options.mappingsOutputFolder == "folder" options.properties == ["foo-bar": "bar", "foo-baz": "baz", "bar.bar": "foo"] + options.httpServerStubConfigurer == Foo + } +} + +class Foo implements HttpServerStubConfigurer { + + @Override + boolean isAccepted(Object httpStubConfiguration) { + return true } } diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy index 591d1f23a6..2f3586b4fe 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy @@ -16,22 +16,30 @@ package org.springframework.cloud.contract.stubrunner.spring +import com.github.tomakehurst.wiremock.core.WireMockConfiguration +import groovy.transform.CompileStatic +import org.apache.commons.logging.Log +import org.apache.commons.logging.LogFactory import org.junit.AfterClass import org.junit.BeforeClass import spock.lang.Issue +import spock.lang.Specification import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value import org.springframework.boot.autoconfigure.EnableAutoConfiguration import org.springframework.boot.test.context.SpringBootContextLoader import org.springframework.boot.test.context.SpringBootTest +import org.springframework.cloud.contract.stubrunner.HttpServerStubConfiguration import org.springframework.cloud.contract.stubrunner.StubFinder import org.springframework.cloud.contract.stubrunner.StubNotFoundException +import org.springframework.cloud.contract.stubrunner.provider.wiremock.WireMockHttpServerStubConfigurer import org.springframework.context.annotation.Configuration import org.springframework.core.env.Environment import org.springframework.test.context.ActiveProfiles import org.springframework.test.context.ContextConfiguration -import spock.lang.Specification +import org.springframework.util.SocketUtils + /** * @author Marcin Grzejszczak */ @@ -42,7 +50,10 @@ import spock.lang.Specification @SpringBootTest(properties = [" stubrunner.cloud.enabled=false", 'foo=${stubrunner.runningstubs.fraudDetectionServer.port}', 'fooWithGroup=${stubrunner.runningstubs.org.springframework.cloud.contract.verifier.stubs.fraudDetectionServer.port}']) -@AutoConfigureStubRunner(mappingsOutputFolder = "target/outputmappings/") +// tag::annotation[] +@AutoConfigureStubRunner(mappingsOutputFolder = "target/outputmappings/", + httpServerStubConfigurer = HttpsForFraudDetection) +// end::annotation[] @ActiveProfiles("test") class StubRunnerConfigurationSpec extends Specification { @@ -74,6 +85,8 @@ class StubRunnerConfigurationSpec extends Specification { and: 'Stubs were registered' "${stubFinder.findStubUrl('loanIssuance').toString()}/name".toURL().text == 'loanIssuance' "${stubFinder.findStubUrl('fraudDetectionServer').toString()}/name".toURL().text == 'fraudDetectionServer' + and: 'Fraud Detection is an HTTPS endpoint' + stubFinder.findStubUrl('fraudDetectionServer').toString().startsWith("https") } def 'should throw an exception when stub is not found'() { @@ -129,5 +142,24 @@ class StubRunnerConfigurationSpec extends Specification { @Configuration @EnableAutoConfiguration static class Config {} + + // tag::wireMockHttpServerStubConfigurer[] + @CompileStatic + static class HttpsForFraudDetection extends WireMockHttpServerStubConfigurer { + + private static final Log log = LogFactory.getLog(HttpsForFraudDetection) + + @Override + WireMockConfiguration configure(WireMockConfiguration httpStubConfiguration, HttpServerStubConfiguration httpServerStubConfiguration) { + if (httpServerStubConfiguration.stubConfiguration.artifactId == "fraudDetectionServer") { + int httpsPort = SocketUtils.findAvailableTcpPort() + log.info("Will set HTTPs port [" + httpsPort + "] for fraud detection server") + return httpStubConfiguration + .httpsPort(httpsPort) + } + return httpStubConfiguration + } + } + // end::wireMockHttpServerStubConfigurer[] } // end::test[] \ No newline at end of file diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockConfiguration.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockConfiguration.java index 37beff5bc6..fe37caf13f 100644 --- a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockConfiguration.java +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockConfiguration.java @@ -138,13 +138,6 @@ public class WireMockConfiguration implements SmartLifecycle { } } - void reset() { - if (log.isDebugEnabled()) { - log.debug("Resetting stubs"); - } - this.server.resetAll(); - } - private void registerFiles( com.github.tomakehurst.wiremock.core.WireMockConfiguration factory) throws IOException {