diff --git a/spring-boot-rest/src/main/java/com/baeldung/spring/ConverterExtensionsConfig.java b/spring-boot-rest/src/main/java/com/baeldung/spring/ConverterExtensionsConfig.java new file mode 100644 index 0000000000..42bf50ac5f --- /dev/null +++ b/spring-boot-rest/src/main/java/com/baeldung/spring/ConverterExtensionsConfig.java @@ -0,0 +1,29 @@ +package com.baeldung.spring; + +import org.springframework.context.annotation.Bean; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.xml.MarshallingHttpMessageConverter; +import org.springframework.oxm.xstream.XStreamMarshaller; + +/** + * Another possibility is to create a bean which will be automatically added to the Spring Boot Autoconfigurations. + * + * ATTENTION: Multiple converter registration of the same type most likely causes problem (serialize twice, etc.) + * Therefore, be sure to remove manually added XML message converter first then uncomment + * this @{@link org.springframework.context.annotation.Configuration} to use + */ +//@Configuration +public class ConverterExtensionsConfig { + + @Bean + public HttpMessageConverter createXmlHttpMessageConverter() { + final MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter(); + + final XStreamMarshaller xstreamMarshaller = new XStreamMarshaller(); + xmlConverter.setMarshaller(xstreamMarshaller); + xmlConverter.setUnmarshaller(xstreamMarshaller); + + return xmlConverter; + } + +} diff --git a/spring-boot-rest/src/main/java/com/baeldung/spring/WebConfig.java b/spring-boot-rest/src/main/java/com/baeldung/spring/WebConfig.java index 13a9933fa6..69724fda29 100644 --- a/spring-boot-rest/src/main/java/com/baeldung/spring/WebConfig.java +++ b/spring-boot-rest/src/main/java/com/baeldung/spring/WebConfig.java @@ -5,7 +5,6 @@ import java.util.List; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.ImportResource; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.xml.MarshallingHttpMessageConverter; @@ -16,35 +15,24 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { - // @Override - // public void configureMessageConverters(final List> messageConverters) { - // messageConverters.add(new MappingJackson2HttpMessageConverter()); - // messageConverters.add(createXmlHttpMessageConverter()); - // } - // - // private HttpMessageConverter createXmlHttpMessageConverter() { - // final MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter(); - // - // final XStreamMarshaller xstreamMarshaller = new XStreamMarshaller(); - // xstreamMarshaller.setAutodetectAnnotations(true); - // xmlConverter.setMarshaller(xstreamMarshaller); - // xmlConverter.setUnmarshaller(xstreamMarshaller); - // - // return xmlConverter; - // } + @Override + public void configureMessageConverters(final List> messageConverters) { + messageConverters.add(new MappingJackson2HttpMessageConverter()); + messageConverters.add(createXmlHttpMessageConverter()); + } - // Another possibility is to create a bean which will be automatically added to the Spring Boot Autoconfigurations - // @Bean - // public HttpMessageConverter createXmlHttpMessageConverter() { - // final MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter(); - // - // final XStreamMarshaller xstreamMarshaller = new XStreamMarshaller(); - // xstreamMarshaller.setAutodetectAnnotations(true); - // xmlConverter.setMarshaller(xstreamMarshaller); - // xmlConverter.setUnmarshaller(xstreamMarshaller); - // - // return xmlConverter; - // } + /** + * There is another possibility to add a message converter, see {@link ConverterExtensionsConfig} + */ + private HttpMessageConverter createXmlHttpMessageConverter() { + final MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter(); + + final XStreamMarshaller xstreamMarshaller = new XStreamMarshaller(); + xmlConverter.setMarshaller(xstreamMarshaller); + xmlConverter.setUnmarshaller(xstreamMarshaller); + + return xmlConverter; + } // Etags @@ -52,16 +40,17 @@ public class WebConfig implements WebMvcConfigurer { // AbstractAnnotationConfigDispatcherServletInitializer#getServletFilters @Bean public FilterRegistrationBean shallowEtagHeaderFilter() { - FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>( new ShallowEtagHeaderFilter()); + FilterRegistrationBean filterRegistrationBean = + new FilterRegistrationBean<>(new ShallowEtagHeaderFilter()); filterRegistrationBean.addUrlPatterns("/foos/*"); filterRegistrationBean.setName("etagFilter"); return filterRegistrationBean; } - + // We can also just declare the filter directly // @Bean // public ShallowEtagHeaderFilter shallowEtagHeaderFilter() { // return new ShallowEtagHeaderFilter(); // } -} \ No newline at end of file +} diff --git a/spring-boot-rest/src/test/java/com/baeldung/SpringContextTest.java b/spring-boot-rest/src/test/java/com/baeldung/SpringContextTest.java deleted file mode 100644 index 9cba7f8fc1..0000000000 --- a/spring-boot-rest/src/test/java/com/baeldung/SpringContextTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.baeldung; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = {SpringBootRestApplication.class}) -public class SpringContextTest { - - @Test - public void contextLoads() { - } - -} diff --git a/spring-boot-rest/src/test/java/com/baeldung/common/web/AbstractBasicLiveTest.java b/spring-boot-rest/src/test/java/com/baeldung/common/web/AbstractBasicLiveTest.java index ecf938be50..6e50f828de 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/common/web/AbstractBasicLiveTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/common/web/AbstractBasicLiveTest.java @@ -24,6 +24,7 @@ import com.google.common.net.HttpHeaders; import io.restassured.RestAssured; import io.restassured.http.ContentType; import io.restassured.response.Response; +import org.springframework.http.MediaType; public abstract class AbstractBasicLiveTest extends AbstractLiveTest { @@ -36,7 +37,7 @@ public abstract class AbstractBasicLiveTest extends Abst @Test public void whenResourcesAreRetrievedPaged_then200IsReceived() { create(); - + final Response response = RestAssured.get(getURL() + "?page=0&size=10"); assertThat(response.getStatusCode(), is(200)); @@ -54,7 +55,8 @@ public abstract class AbstractBasicLiveTest extends Abst public void givenResourcesExist_whenFirstPageIsRetrieved_thenPageContainsResources() { create(); - final Response response = RestAssured.get(getURL() + "?page=0&size=10"); + final Response response = RestAssured.given() + .accept(MediaType.APPLICATION_JSON_VALUE).get(getURL() + "?page=0&size=10"); assertFalse(response.body().as(List.class).isEmpty()); } @@ -64,7 +66,7 @@ public abstract class AbstractBasicLiveTest extends Abst create(); create(); create(); - + final Response response = RestAssured.get(getURL() + "?page=0&size=2"); final String uriToNextPage = extractURIByRel(response.getHeader(HttpHeaders.LINK), "next"); @@ -95,7 +97,7 @@ public abstract class AbstractBasicLiveTest extends Abst create(); create(); create(); - + final Response first = RestAssured.get(getURL() + "?page=0&size=2"); final String uriToLastPage = extractURIByRel(first.getHeader(HttpHeaders.LINK), "last"); @@ -104,7 +106,7 @@ public abstract class AbstractBasicLiveTest extends Abst final String uriToNextPage = extractURIByRel(response.getHeader(HttpHeaders.LINK), "next"); assertNull(uriToNextPage); } - + // etags @Test diff --git a/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerAppIntegrationTest.java b/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerAppIntegrationTest.java index 3300b91fde..b1a84b47a7 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerAppIntegrationTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerAppIntegrationTest.java @@ -1,21 +1,22 @@ package com.baeldung.web; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.baeldung.persistence.dao.IFooDao; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; - /** - * - * We'll start the whole context, but not the server. We'll mock the REST calls instead. - * + * We'll start the whole context, but not the server. We'll mock the REST calls instead. */ @RunWith(SpringRunner.class) @SpringBootTest @@ -25,12 +26,22 @@ public class FooControllerAppIntegrationTest { @Autowired private MockMvc mockMvc; + @Autowired + private IFooDao fooDao; + + @Before + public void setup() { + this.fooDao.deleteAll(); + } + @Test public void whenFindPaginatedRequest_thenEmptyResponse() throws Exception { - this.mockMvc.perform(get("/foos").param("page", "0") - .param("size", "2")) - .andExpect(status().isOk()) - .andExpect(content().json("[]")); + this.mockMvc.perform(get("/foos") + .param("page", "0") + .param("size", "2") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().json("[]")); } } diff --git a/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerCustomEtagIntegrationTest.java b/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerCustomEtagIntegrationTest.java index 9e7b60ed8c..e472d308e8 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerCustomEtagIntegrationTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerCustomEtagIntegrationTest.java @@ -40,7 +40,7 @@ public class FooControllerCustomEtagIntegrationTest { private static String createFooJson() throws Exception { return serializeFoo(new Foo(randomAlphabetic(6))); } - + private static Foo deserializeFoo(String fooJson) throws Exception { ObjectMapper mapper = new ObjectMapper(); return mapper.readValue(fooJson, Foo.class); @@ -97,7 +97,8 @@ public class FooControllerCustomEtagIntegrationTest { .getResponse() .getHeader(HttpHeaders.LOCATION); ResultActions findOneResponse = this.mvc - .perform(get(createdResourceUri + CUSTOM_ETAG_ENDPOINT_SUFFIX).contentType(MediaType.APPLICATION_JSON)); + .perform(get(createdResourceUri + CUSTOM_ETAG_ENDPOINT_SUFFIX) + .contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON)); String etag = findOneResponse.andReturn().getResponse().getHeader(HttpHeaders.ETAG); Foo createdFoo = deserializeFoo(findOneResponse.andReturn().getResponse().getContentAsString()); createdFoo.setName("updated name"); diff --git a/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerWebLayerIntegrationTest.java b/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerWebLayerIntegrationTest.java index 4d4a274b1a..070625b7d4 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerWebLayerIntegrationTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerWebLayerIntegrationTest.java @@ -20,6 +20,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; +import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; @@ -30,7 +31,7 @@ import com.baeldung.web.exception.CustomException1; import com.baeldung.web.hateoas.event.PaginatedResultsRetrievedEvent; /** - * + * * We'll start only the web layer. * */ @@ -54,20 +55,22 @@ public class FooControllerWebLayerIntegrationTest { doNothing().when(publisher) .publishEvent(any(PaginatedResultsRetrievedEvent.class)); - this.mockMvc.perform(get("/foos").param("page", "0") - .param("size", "2")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.hasSize(1))); + this.mockMvc.perform(get("/foos") + .param("page", "0") + .param("size", "2") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.hasSize(1))); } - + @Test public void delete_forException_fromService() throws Exception { Mockito.when(service.findAll()).thenThrow(new CustomException1()); this.mockMvc.perform(get("/foos")).andDo(h -> { final Exception expectedException = h.getResolvedException(); Assert.assertTrue(expectedException instanceof CustomException1); - + }); } - + } diff --git a/spring-boot-rest/src/test/java/com/baeldung/web/FooMessageConvertersLiveTest.java b/spring-boot-rest/src/test/java/com/baeldung/web/FooMessageConvertersLiveTest.java new file mode 100644 index 0000000000..9b1a9e9733 --- /dev/null +++ b/spring-boot-rest/src/test/java/com/baeldung/web/FooMessageConvertersLiveTest.java @@ -0,0 +1,149 @@ +package com.baeldung.web; + +import static com.baeldung.Consts.APPLICATION_PORT; +import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import com.baeldung.common.web.AbstractLiveTest; +import com.baeldung.persistence.model.Foo; +import com.baeldung.spring.ConfigIntegrationTest; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.http.converter.xml.MarshallingHttpMessageConverter; +import org.springframework.oxm.xstream.XStreamMarshaller; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; +import org.springframework.web.client.RestTemplate; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { ConfigIntegrationTest.class }, loader = AnnotationConfigContextLoader.class) +@ActiveProfiles("test") +public class FooMessageConvertersLiveTest extends AbstractLiveTest { + + private static final String BASE_URI = "http://localhost:" + APPLICATION_PORT + "/spring-boot-rest/"; + + public FooMessageConvertersLiveTest() { + super(Foo.class); + } + + @Override + public final void create() { + create(new Foo(randomAlphabetic(6))); + } + + @Override + public final String createAsUri() { + return createAsUri(new Foo(randomAlphabetic(6))); + } + + @Before + public void setup(){ + create(); + } + + /** + * Without specifying Accept Header, uses the default response from the + * server (in this case json) + */ + @Test + public void whenRetrievingAFoo_thenCorrect() { + final String URI = BASE_URI + "foos/{id}"; + + final RestTemplate restTemplate = new RestTemplate(); + final Foo resource = restTemplate.getForObject(URI, Foo.class, "1"); + + assertThat(resource, notNullValue()); + } + + @Test + public void givenConsumingXml_whenReadingTheFoo_thenCorrect() { + final String URI = BASE_URI + "foos/{id}"; + + final RestTemplate restTemplate = new RestTemplate(); + restTemplate.setMessageConverters(getXmlMessageConverters()); + + final HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_XML)); + final HttpEntity entity = new HttpEntity<>(headers); + + final ResponseEntity + response = restTemplate.exchange(URI, HttpMethod.GET, entity, Foo.class, "1"); + final Foo resource = response.getBody(); + + assertThat(resource, notNullValue()); + } + + private List> getXmlMessageConverters() { + final XStreamMarshaller marshaller = new XStreamMarshaller(); + marshaller.setAnnotatedClasses(Foo.class); + final MarshallingHttpMessageConverter marshallingConverter = new MarshallingHttpMessageConverter(marshaller); + + final List> converters = new ArrayList<>(); + converters.add(marshallingConverter); + return converters; + } + + @Test + public void givenConsumingJson_whenReadingTheFoo_thenCorrect() { + final String URI = BASE_URI + "foos/{id}"; + + final RestTemplate restTemplate = new RestTemplate(); + restTemplate.setMessageConverters(getJsonMessageConverters()); + + final HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + final HttpEntity entity = new HttpEntity(headers); + + final ResponseEntity response = restTemplate.exchange(URI, HttpMethod.GET, entity, Foo.class, "1"); + final Foo resource = response.getBody(); + + assertThat(resource, notNullValue()); + } + + private List> getJsonMessageConverters() { + final List> converters = new ArrayList<>(); + converters.add(new MappingJackson2HttpMessageConverter()); + return converters; + } + + @Test + public void givenConsumingXml_whenWritingTheFoo_thenCorrect() { + final String URI = BASE_URI + "foos"; + final RestTemplate restTemplate = new RestTemplate(); + restTemplate.setMessageConverters(getJsonAndXmlMessageConverters()); + + final Foo resource = new Foo("jason"); + final HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + headers.setContentType((MediaType.APPLICATION_XML)); + final HttpEntity entity = new HttpEntity<>(resource, headers); + + final ResponseEntity response = restTemplate.exchange(URI, HttpMethod.POST, entity, Foo.class); + final Foo fooResponse = response.getBody(); + + assertThat(fooResponse, notNullValue()); + assertEquals(resource.getName(), fooResponse.getName()); + } + + private List> getJsonAndXmlMessageConverters() { + final List> converters = getJsonMessageConverters(); + converters.addAll(getXmlMessageConverters()); + return converters; + } + +} diff --git a/spring-boot-rest/src/test/java/com/baeldung/web/FooPageableLiveTest.java b/spring-boot-rest/src/test/java/com/baeldung/web/FooPageableLiveTest.java index 6a365f3bd5..242fbb609e 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/web/FooPageableLiveTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/web/FooPageableLiveTest.java @@ -11,6 +11,7 @@ import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.http.MediaType; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -43,12 +44,12 @@ public class FooPageableLiveTest extends AbstractBasicLiveTest { public final String createAsUri() { return createAsUri(new Foo(randomAlphabetic(6))); } - + @Override @Test public void whenResourcesAreRetrievedPaged_then200IsReceived() { this.create(); - + final Response response = RestAssured.get(getPageableURL() + "?page=0&size=10"); assertThat(response.getStatusCode(), is(200)); @@ -68,13 +69,15 @@ public class FooPageableLiveTest extends AbstractBasicLiveTest { public void givenResourcesExist_whenFirstPageIsRetrieved_thenPageContainsResources() { create(); - final Response response = RestAssured.get(getPageableURL() + "?page=0&size=10"); + final Response response = RestAssured.given() + .accept(MediaType.APPLICATION_JSON_VALUE) + .get(getPageableURL() + "?page=0&size=10"); assertFalse(response.body().as(List.class).isEmpty()); } protected String getPageableURL() { - return "http://localhost:" + APPLICATION_PORT + "/spring-boot-rest/foos/pageable"; + return getURL() + "/pageable"; } - + }