diff --git a/README.adoc b/README.adoc index bf075d806d..46633f445c 100644 --- a/README.adoc +++ b/README.adoc @@ -5,6 +5,7 @@ image::https://badges.gitter.im/Join%20Chat.svg[Gitter, link="https://gitter.im/ image::https://codecov.io/gh/spring-cloud/spring-cloud-contract/branch/{branch}/graph/badge.svg["codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-contract"] image::https://circleci.com/gh/spring-cloud/spring-cloud-contract.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-contract"] :introduction_url: ../../../.. +:verifier_core_path: {introduction_url}/spring-cloud-contract-verifier == Spring Cloud Contract @@ -331,14 +332,33 @@ The following example shows a Camel messaging contract expressed in Groovy DSL: [source,groovy] ---- -Unresolved directive in verifier_introduction.adoc - include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_no_output_dsl] +def contractDsl = Contract.make { + label 'some_label' + input { + messageFrom('jms:delete') + messageBody([ + bookName: 'foo' + ]) + messageHeaders { + header('sample', 'header') + } + assertThat('bookWasDeleted()') + } +} ---- The following example shows the same contract expressed in YAML: [source,yml,indent=0] ---- -Unresolved directive in verifier_introduction.adoc - include::{verifier_core_path}/src/test/resources/yml/contract_message_scenario3.yml[indent=0] +label: some_label +input: + messageFrom: jms:delete + messageBody: + bookName: 'foo' + messageHeaders: + sample: header + assertThat: bookWasDeleted() ---- Then you can add Spring Cloud Contract Verifier dependency and plugin to your build file, diff --git a/docs/src/main/asciidoc/README.adoc b/docs/src/main/asciidoc/README.adoc index cebcc2184f..554865b748 100644 --- a/docs/src/main/asciidoc/README.adoc +++ b/docs/src/main/asciidoc/README.adoc @@ -3,6 +3,7 @@ image::https://badges.gitter.im/Join%20Chat.svg[Gitter, link="https://gitter.im/ image::https://codecov.io/gh/spring-cloud/spring-cloud-contract/branch/{branch}/graph/badge.svg["codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-contract"] image::https://circleci.com/gh/spring-cloud/spring-cloud-contract.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-contract"] :introduction_url: ../../../.. +:verifier_core_path: {introduction_url}/spring-cloud-contract-verifier == Spring Cloud Contract diff --git a/docs/src/main/asciidoc/verifier_faq.adoc b/docs/src/main/asciidoc/verifier_faq.adoc index 10a5ccc3ae..13d24a3bc5 100644 --- a/docs/src/main/asciidoc/verifier_faq.adoc +++ b/docs/src/main/asciidoc/verifier_faq.adoc @@ -936,4 +936,40 @@ started will be attached. Yes! With version 1.2.0 we've added such a possibility. It's enough to call `file(...)` method in the DSL and provide a path relative to where the contract lays. -If you're using YAML just use the `bodyFromFile` property. \ No newline at end of file +If you're using YAML just use the `bodyFromFile` property. + +==== Why do I sometimes get `SocketException` + +When using an HTTP client (e.g. `RestTemplate`) and running several tests that share a Spring context, you might sometimes get the following exception: + +``` +java.net.SocketException: Unexpected end of file from server +``` + +Tom Akehurst, the creator of WireMock did the following analysis of this issue. + +> I looked at tcpdump while running the failing test. `HttpUrlConnection` is doing something weird - it's creating a connection in a previous test case, which works fine, then the usual `fin` -> `fin ack` etc. ending handshake happens. But it seems it isn't discarded, but reused after that. Because the server thinks (rightly) that the connection is closed, it just sends a RST packet. Calling the `/__admin` endpoint just happened to remove the dead connection from the pool. This also fixes the problem (which using the Java HTTP client): System.setProperty("http.keepAlive", "false"); + +There are the ways to solve this problem. + +First, just use a different HTTP client for `RestTemplate`. Example for using Apache HTTP client: + +.pom.xml +[source,xml,indent=0] +---- +include::{standalone_restdocs_path}/http-client/pom.xml[tags=httpclient,indent=0] +---- + +[source,java,indent=0] +---- +include::{standalone_restdocs_path}/http-client/src/main/java/com/example/loan/LoanApplicationService.java[tags=custom_request_factory,indent=0] +---- + +Second option is to set the system property. You can set it either in code or pass it to your tests via a plugin. + +[source,java,indent=0] +---- +static { + System.setProperty("http.keepAlive", "false"); +} +---- \ No newline at end of file diff --git a/samples/standalone/restdocs/http-client/pom.xml b/samples/standalone/restdocs/http-client/pom.xml index 7ff7dd6969..09680c9f50 100644 --- a/samples/standalone/restdocs/http-client/pom.xml +++ b/samples/standalone/restdocs/http-client/pom.xml @@ -1,4 +1,21 @@ + + 4.0.0 @@ -53,6 +70,13 @@ spring-cloud-contract-stub-runner test + + + org.apache.httpcomponents + httpclient + compile + + com.example http-server-restdocs diff --git a/samples/standalone/restdocs/http-client/src/main/java/com/example/loan/LoanApplicationService.java b/samples/standalone/restdocs/http-client/src/main/java/com/example/loan/LoanApplicationService.java index 6c5d0861f5..9f3c759b9e 100644 --- a/samples/standalone/restdocs/http-client/src/main/java/com/example/loan/LoanApplicationService.java +++ b/samples/standalone/restdocs/http-client/src/main/java/com/example/loan/LoanApplicationService.java @@ -1,12 +1,21 @@ -package com.example.loan; +/* + * 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. + * + */ -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.web.client.RestTemplate; +package com.example.loan; import com.example.loan.model.FraudCheckStatus; import com.example.loan.model.FraudServiceRequest; @@ -15,6 +24,15 @@ import com.example.loan.model.LoanApplication; import com.example.loan.model.LoanApplicationResult; import com.example.loan.model.LoanApplicationStatus; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + @Service @ConfigurationProperties("service") public class LoanApplicationService { @@ -28,6 +46,10 @@ public class LoanApplicationService { public LoanApplicationService() { this.restTemplate = new RestTemplate(); + // tag::custom_request_factory[] + this.restTemplate + .setRequestFactory(new HttpComponentsClientHttpRequestFactory()); + // end::custom_request_factory[] } public LoanApplicationResult loanApplication(LoanApplication loanApplication) { diff --git a/samples/standalone/restdocs/http-client/src/test/java/com/example/loan/XmlServiceTests.java b/samples/standalone/restdocs/http-client/src/test/java/com/example/loan/XmlServiceTests.java index 351ce62021..77e0b2682b 100644 --- a/samples/standalone/restdocs/http-client/src/test/java/com/example/loan/XmlServiceTests.java +++ b/samples/standalone/restdocs/http-client/src/test/java/com/example/loan/XmlServiceTests.java @@ -1,3 +1,20 @@ +/* + * 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 com.example.loan; import java.net.URI; @@ -8,6 +25,7 @@ import com.github.tomakehurst.wiremock.stubbing.StubMapping; import org.assertj.core.api.BDDAssertions; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; diff --git a/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner/pom.xml b/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner/pom.xml index c071f6ac50..9d1ef620ff 100644 --- a/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner/pom.xml +++ b/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner/pom.xml @@ -1,4 +1,21 @@ + + 4.0.0 @@ -20,6 +37,7 @@ org.springframework.boot spring-boot-starter-web + true org.springframework.boot diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/StubRunnerWireMockTestExecutionListener.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/StubRunnerWireMockTestExecutionListener.java index 7bd3005ffa..8b777c471d 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/StubRunnerWireMockTestExecutionListener.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/StubRunnerWireMockTestExecutionListener.java @@ -1,3 +1,20 @@ +/* + * 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 java.util.List; @@ -7,12 +24,13 @@ import java.util.concurrent.ConcurrentHashMap; import com.github.tomakehurst.wiremock.stubbing.StubMapping; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.cloud.contract.stubrunner.HttpServerStub; +import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner; +import org.springframework.cloud.contract.wiremock.WireMockUtils; import org.springframework.context.ApplicationContext; import org.springframework.test.context.TestContext; import org.springframework.test.context.support.AbstractTestExecutionListener; -import org.springframework.util.Assert; -import org.springframework.web.client.RestTemplate; /** * Stops the {@link HttpServerStub} after each test class @@ -20,46 +38,55 @@ import org.springframework.web.client.RestTemplate; * @author Marcin Grzejszczak * @since 1.2.6 */ -public final class StubRunnerWireMockTestExecutionListener extends AbstractTestExecutionListener { +public final class StubRunnerWireMockTestExecutionListener + extends AbstractTestExecutionListener { - private static final Log log = LogFactory.getLog(StubRunnerWireMockTestExecutionListener.class); + private static final Log log = LogFactory + .getLog(StubRunnerWireMockTestExecutionListener.class); private static Map> STUBS = new ConcurrentHashMap<>(); - @Override public void beforeTestClass(TestContext testContext) { + @Override + public void beforeTestClass(TestContext testContext) { + if (testContext.getTestClass().getAnnotationsByType(AutoConfigureStubRunner.class).length == 0) { + if (log.isTraceEnabled()) { + log.trace("No @AutoConfigureStubRunner annotation found on [" + testContext.getTestClass() + "]. Skipping"); + } + return; + } Map stubs = STUBS .get(testContext.getApplicationContext()); if (stubs != null) { if (log.isDebugEnabled()) { - log.debug("Found a matching application context from [" + testContext.getTestClass().getName() + "]"); + log.debug("Found a matching application context from [" + + testContext.getTestClass().getName() + "]"); } - for (Map.Entry entry : stubs.entrySet()) { + for (Map.Entry entry : stubs + .entrySet()) { while (entry.getKey().isRunning()) { entry.getKey().stop(); } List mappings = entry.getValue().mappings; if (log.isDebugEnabled()) { - log.debug("Stopped a running WireMock instance at " - + "port [" + entry.getValue().port + "] with stub mappings " - + "size [" + mappings.size() + "]. Restarting the stub."); + log.debug("Stopped a running WireMock instance at " + "port [" + + entry.getValue().port + "] with stub mappings size [" + + mappings.size() + "]. Restarting the stub."); } entry.getKey().start(entry.getValue().port); entry.getKey().registerDescriptors(mappings); - /* - Thanks to Tom Akehurst: - I looked at tcpdump while running the failing test. HttpUrlConnection is doing something weird - it's creating a connection in a - previous test case, which works fine, then the usual fin -> fin ack etc. etc. ending handshake happens. But it seems it - isn't discarded, but reused after that. Because the server thinks (rightly) that the connection is closed, it just sends a RST packet. - Calling the admin endpoint just happened to remove the dead connection from the pool. - This also fixes the problem (which using the Java HTTP client): System.setProperty("http.keepAlive", "false"); - */ - Assert.isTrue(new RestTemplate().getForEntity("http://localhost:" + entry.getValue().port + "/__admin/mappings", String.class) - .getStatusCode().is2xxSuccessful(), "__admin/mappings endpoint wasn't accessible"); + WireMockUtils.getMappingsEndpoint(entry.getValue().port); } } } - @Override public void afterTestClass(TestContext testContext) { + @Override + public void afterTestClass(TestContext testContext) { + if (testContext.getTestClass().getAnnotationsByType(AutoConfigureStubRunner.class).length == 0) { + if (log.isTraceEnabled()) { + log.trace("No @AutoConfigureStubRunner annotation found on [" + testContext.getTestClass() + "]. Skipping"); + } + return; + } STUBS.put(testContext.getApplicationContext(), WireMockHttpServerStub.SERVERS); if (log.isDebugEnabled()) { log.debug("Stopping servers " + WireMockHttpServerStub.SERVERS); 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 e08a310f52..747e34b503 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 @@ -1,3 +1,20 @@ +/* + * 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 java.io.File; @@ -20,6 +37,8 @@ import com.github.tomakehurst.wiremock.extension.Extension; 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.verifier.builder.handlebars.HandlebarsEscapeHelper; import org.springframework.cloud.contract.verifier.builder.handlebars.HandlebarsJsonPathHelper; @@ -31,7 +50,6 @@ import org.springframework.util.ClassUtils; import org.springframework.util.SocketUtils; import org.springframework.util.StreamUtils; import org.springframework.util.StringUtils; -import wiremock.com.github.jknack.handlebars.Helper; /** * Abstraction over WireMock as a HTTP Server Stub @@ -115,7 +133,7 @@ public class WireMockHttpServerStub implements HttpServerStub { log.debug("Started WireMock at port [" + port + "]"); } if (!SERVERS.containsKey(this)) { - SERVERS.put(this, new PortAndMappings(port, new ArrayList())); + SERVERS.put(this, new PortAndMappings(port, new ArrayList<>())); } return this; } 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 b75f391fee..8ac5ea3a3e 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 @@ -1,17 +1,18 @@ /* - * Copyright 2012-2015 the original author or authors. + * 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 + * 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.wiremock; @@ -20,6 +21,7 @@ import java.io.IOException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; + import javax.annotation.PostConstruct; import com.github.tomakehurst.wiremock.WireMockServer; @@ -28,6 +30,7 @@ import com.github.tomakehurst.wiremock.common.Slf4jNotifier; import com.github.tomakehurst.wiremock.core.Options; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -38,10 +41,8 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -import org.springframework.util.Assert; import org.springframework.util.StreamUtils; import org.springframework.util.StringUtils; -import org.springframework.web.client.RestTemplate; /** * Configuration and lifecycle for a Spring Application context that wants to run a @@ -50,7 +51,7 @@ import org.springframework.web.client.RestTemplate; * configure the properties of the wiremock server you can use the AutoConfigureWireMock * annotation, or add a bean of type {@link Options} (via * {@link WireMockSpring#options()}) to your test context. - * + * * @author Dave Syer * */ @@ -103,7 +104,8 @@ public class WireMockConfiguration implements SmartLifecycle { } registerStubs(); if (log.isDebugEnabled()) { - log.debug("WireMock server has [" + this.server.getStubMappings().size() + "] registered"); + log.debug("WireMock server has [" + this.server.getStubMappings().size() + + "] stubs registered"); } if (!this.beanFactory.containsBean(WIREMOCK_SERVER_BEAN_NAME)) { this.beanFactory.registerSingleton(WIREMOCK_SERVER_BEAN_NAME, this.server); @@ -130,6 +132,10 @@ public class WireMockConfiguration implements SmartLifecycle { } } + int port() { + return this.server.port(); + } + void reset() { this.server.resetAll(); } @@ -161,22 +167,14 @@ public class WireMockConfiguration implements SmartLifecycle { if (log.isDebugEnabled()) { log.debug("Started WireMock at port [" + this.server.port() + "]. It has [" + this.server.getStubMappings().size() + "] mappings registered"); } - /* - Thanks to Tom Akehurst: - I looked at tcpdump while running the failing test. HttpUrlConnection is doing something weird - it's creating a connection in a - previous test case, which works fine, then the usual fin -> fin ack etc. etc. ending handshake happens. But it seems it - isn't discarded, but reused after that. Because the server thinks (rightly) that the connection is closed, it just sends a RST packet. - Calling the admin endpoint just happened to remove the dead connection from the pool. - This also fixes the problem (which using the Java HTTP client): System.setProperty("http.keepAlive", "false"); - */ - Assert.isTrue(new RestTemplate().getForEntity("http://localhost:" + this.server.port() + "/__admin/mappings", String.class) - .getStatusCode().is2xxSuccessful(), "__admin/mappings endpoint wasn't accessible"); + WireMockUtils.getMappingsEndpoint(this.port()); } @Override public void stop() { if (this.running) { - this.server.stop(); + reset(); + this.server.shutdownServer(); this.running = false; if (log.isDebugEnabled()) { log.debug("Stopped WireMock instance"); diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockTestExecutionListener.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockTestExecutionListener.java index 77947f65bf..fcddd23755 100644 --- a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockTestExecutionListener.java +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockTestExecutionListener.java @@ -1,7 +1,25 @@ +/* + * 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.wiremock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.test.context.TestContext; import org.springframework.test.context.support.AbstractTestExecutionListener; @@ -25,9 +43,9 @@ public final class WireMockTestExecutionListener extends AbstractTestExecutionLi log.debug("WireMock configuration is running [" + wireMockConfiguration.isRunning() + "]"); } if (!wireMockConfiguration.isRunning()) { - wireMockConfiguration.reset(); wireMockConfiguration.init(); wireMockConfiguration.start(); + WireMockUtils.getMappingsEndpoint(wireMockConfiguration.port()); } } catch (Exception e) { if (log.isDebugEnabled()) { diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockUtils.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockUtils.java new file mode 100644 index 0000000000..5b700ff9c8 --- /dev/null +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockUtils.java @@ -0,0 +1,65 @@ +/* + * 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.wiremock; + +import java.io.IOException; + +import org.apache.http.HttpHost; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; + +import org.springframework.util.Assert; + +/** + * Utility class to work with WireMock. + * + * @author Marcin Grzejszczak + * @since 2.0.3 + */ +public final class WireMockUtils { + + private WireMockUtils() { + throw new IllegalStateException("Don't instantiate"); + } + + /** + * Thanks to Tom Akehurst: I looked at tcpdump while running the failing + * test. HttpUrlConnection is doing something weird - it's creating a + * connection in a previous test case, which works fine, then the usual + * fin -> fin ack etc. etc. ending handshake happens. But it seems it + * isn't discarded, but reused after that. Because the server thinks + * (rightly) that the connection is closed, it just sends a RST packet. + * Calling the admin endpoint just happened to remove the dead connection + * from the pool. This also fixes the problem (which using the Java HTTP + * client): System.setProperty("http.keepAlive", "false"); + **/ + public static CloseableHttpResponse getMappingsEndpoint(int port) { + CloseableHttpClient client = HttpClientBuilder.create().build(); + try { + CloseableHttpResponse response = client + .execute(new HttpHost("localhost", port), new HttpGet("/__admin/mappings")); + Assert.isTrue(response.getStatusLine().getStatusCode() == 200, "Status code must be 200"); + return response; + } + catch (IOException ex) { + throw new IllegalStateException(ex); + } + } +}