diff --git a/SPR-13733/README.md b/SPR-13733/README.md new file mode 100644 index 00000000..f896f24b --- /dev/null +++ b/SPR-13733/README.md @@ -0,0 +1,3 @@ +# Repro project for SPR-13733 + +Please, run org.springframework.issues.spr13733.WitcherResourceTest to reproduce the issue \ No newline at end of file diff --git a/SPR-13733/pom.xml b/SPR-13733/pom.xml new file mode 100644 index 00000000..0a44ef6f --- /dev/null +++ b/SPR-13733/pom.xml @@ -0,0 +1,90 @@ + + 4.0.0 + + + spring-boot-starter-parent + org.springframework.boot + 1.3.0.RELEASE + + + + org.springframework.issues + SPR-13733 + 1.0-SNAPSHOT + Spring MVC Issue Reproduction Project + war + + + UTF-8 + ${java.version} + ${java.version} + 1.8 + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-tomcat + + + org.springframework + spring-context-support + + + org.springframework.boot + spring-boot-autoconfigure + + + org.springframework.boot + spring-boot-loader-tools + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.springframework.boot + spring-boot-starter-logging + + + org.springframework.boot + spring-boot-starter-test + + + + org.apache.httpcomponents + httpclient + + + + org.assertj + assertj-core + 3.2.0 + + + + + + spring-maven-snapshot + Springframework Maven Snapshot Repository + http://repo.spring.io/snapshot + + true + + + + + + diff --git a/SPR-13733/src/main/java/org/springframework/issues/spr13733/Application.java b/SPR-13733/src/main/java/org/springframework/issues/spr13733/Application.java new file mode 100644 index 00000000..504f2a19 --- /dev/null +++ b/SPR-13733/src/main/java/org/springframework/issues/spr13733/Application.java @@ -0,0 +1,20 @@ +package org.springframework.issues.spr13733; + +import java.net.UnknownHostException; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ComponentScan +@EnableAutoConfiguration +public class Application { + + public static void main(final String[] args) throws UnknownHostException { + final SpringApplication app = new SpringApplication(Application.class); + app.run(args); + } + +} diff --git a/SPR-13733/src/main/java/org/springframework/issues/spr13733/config/MockClientHttpRequestFactoryConfiguration.java b/SPR-13733/src/main/java/org/springframework/issues/spr13733/config/MockClientHttpRequestFactoryConfiguration.java new file mode 100644 index 00000000..f664597f --- /dev/null +++ b/SPR-13733/src/main/java/org/springframework/issues/spr13733/config/MockClientHttpRequestFactoryConfiguration.java @@ -0,0 +1,26 @@ +package org.springframework.issues.spr13733.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.test.web.client.MockMvcClientHttpRequestFactory; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +@Configuration +public class MockClientHttpRequestFactoryConfiguration { + + @Autowired + protected WebApplicationContext wac; + + @Bean + public ClientHttpRequestFactory clientHttpRequestFactory() { + final MockMvc mockMvc = MockMvcBuilders + .webAppContextSetup(this.wac) + .build(); + return new MockMvcClientHttpRequestFactory(mockMvc); + } + +} diff --git a/SPR-13733/src/main/java/org/springframework/issues/spr13733/config/RestClientConfiguration.java b/SPR-13733/src/main/java/org/springframework/issues/spr13733/config/RestClientConfiguration.java new file mode 100644 index 00000000..5da0917c --- /dev/null +++ b/SPR-13733/src/main/java/org/springframework/issues/spr13733/config/RestClientConfiguration.java @@ -0,0 +1,22 @@ +package org.springframework.issues.spr13733.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestClientConfiguration { + + @Autowired + private ClientHttpRequestFactory clientHttpRequestFactory; + + @Bean + public RestOperations restOperations() { + final RestTemplate restTemplate = new RestTemplate(); + restTemplate.setRequestFactory(clientHttpRequestFactory); + return restTemplate; + } +} diff --git a/SPR-13733/src/main/java/org/springframework/issues/spr13733/web/WitcherResource.java b/SPR-13733/src/main/java/org/springframework/issues/spr13733/web/WitcherResource.java new file mode 100644 index 00000000..ddbd97cf --- /dev/null +++ b/SPR-13733/src/main/java/org/springframework/issues/spr13733/web/WitcherResource.java @@ -0,0 +1,28 @@ +package org.springframework.issues.spr13733.web; + +import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.web.bind.annotation.RequestMethod.POST; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.MultiValueMap; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/witcher/contract") +public class WitcherResource { + + private static final Logger log = LoggerFactory.getLogger(WitcherResource.class); + + @RequestMapping(value = "validate", method = POST, consumes = APPLICATION_FORM_URLENCODED_VALUE, produces = APPLICATION_JSON_VALUE) + public WitcherResponse validateContract(@RequestBody final MultiValueMap data) { + log.info("Witcher contract: {}", data); + + final WitcherResponse witcherResponse = new WitcherResponse(); + witcherResponse.setAccepted(true); + return witcherResponse; + } +} diff --git a/SPR-13733/src/main/java/org/springframework/issues/spr13733/web/WitcherResponse.java b/SPR-13733/src/main/java/org/springframework/issues/spr13733/web/WitcherResponse.java new file mode 100644 index 00000000..500a9a9b --- /dev/null +++ b/SPR-13733/src/main/java/org/springframework/issues/spr13733/web/WitcherResponse.java @@ -0,0 +1,14 @@ +package org.springframework.issues.spr13733.web; + +public final class WitcherResponse { + + private boolean accepted; + + public boolean isAccepted() { + return accepted; + } + + public void setAccepted(final boolean accepted) { + this.accepted = accepted; + } +} diff --git a/SPR-13733/src/test/java/org/springframework/issues/spr13733/WitcherResourceTest.java b/SPR-13733/src/test/java/org/springframework/issues/spr13733/WitcherResourceTest.java new file mode 100644 index 00000000..97cad5cd --- /dev/null +++ b/SPR-13733/src/test/java/org/springframework/issues/spr13733/WitcherResourceTest.java @@ -0,0 +1,66 @@ +package org.springframework.issues.spr13733; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.IntegrationTest; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.issues.spr13733.web.WitcherResponse; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = Application.class) +@WebAppConfiguration +@IntegrationTest +public class WitcherResourceTest { + + private static final boolean SEND_WITH_PARAMS_IN_URL = true; + + private static final boolean SEND_WITH_PARAMS_IN_BODY = false; + + @Autowired + private RestOperations restOperations; + + private void shouldAcceptContract(final boolean sendWithParamsInUrl) { + // given + String url = "/witcher/contract/validate"; + + final String contract = "The Apiarian Phantom"; + final String beast = "Wild Hunt Hound"; + final String reward = "120 crowns"; + + final MultiValueMap parameters = new LinkedMultiValueMap<>(); + + if (sendWithParamsInUrl) { + url += "?contract=" + contract + "&beast=" + beast + "&reward=" + reward; + } else { + parameters.add("contract", contract); + parameters.add("beast", beast); + parameters.add("reward", reward); + } + + // when + final WitcherResponse witcherResponse = restOperations + .postForObject(url, parameters, WitcherResponse.class); + + // then + assertThat(witcherResponse.isAccepted()).isTrue(); + } + + @Test + public void shouldAcceptContractWhenPostRequestSentWithParamsInUrl() throws Exception { + shouldAcceptContract(SEND_WITH_PARAMS_IN_URL); + } + + @Test + public void shouldAcceptContractWhenPostRequestWithParamsInBody() throws Exception { + shouldAcceptContract(SEND_WITH_PARAMS_IN_BODY); + } + +}