From: Gustavo Martin Morcuende Date: Sun, 5 Feb 2017 17:33:44 +0000 (+0100) Subject: Spring Boot REST X-Git-Url: https://git.gumartinm.name/?a=commitdiff_plain;h=ba7aef213735b5dbfa9b3649564ea495470d4b54;p=SpringWebServicesForFun%2F.git Spring Boot REST --- diff --git a/REST/Spring/web-services-spring-rest-client/pom.xml b/REST/Spring/web-services-spring-rest-client/pom.xml new file mode 100644 index 0000000..276b3db --- /dev/null +++ b/REST/Spring/web-services-spring-rest-client/pom.xml @@ -0,0 +1,61 @@ + + 4.0.0 + + web-services-spring-rest + de.spring.webservices + 1.0-SNAPSHOT + + web-services-spring-rest-client + web-services-spring-rest-client + http://gumartinm.name + + + de.spring.webservices + web-services-spring-rest-global + 1.0-SNAPSHOT + + + + org.springframework + spring-core + + + org.springframework + spring-webmvc + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + junit + junit + test + + + org.springframework + spring-test + test + + + org.mockito + mockito-core + test + + + + + + ${basedir}/src/main/resources/ + + **/*.* + + + + + \ No newline at end of file diff --git a/REST/Spring/web-services-spring-rest-client/src/main/java/de/spring/webservices/main/MainTest.java b/REST/Spring/web-services-spring-rest-client/src/main/java/de/spring/webservices/main/MainTest.java new file mode 100644 index 0000000..0f066ad --- /dev/null +++ b/REST/Spring/web-services-spring-rest-client/src/main/java/de/spring/webservices/main/MainTest.java @@ -0,0 +1,35 @@ +package de.spring.webservices.main; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +import de.spring.webservices.rest.business.service.BusinessService; + + +/** + * This class is used just like a nice example about how to write and run client + * code which will send data to and from the Web Services. + * + */ +public class MainTest { + public ApplicationContext context; + + /** + * @param args + */ + public static void main(final String[] args) { + final MainTest test = new MainTest(); + + test.context = new ClassPathXmlApplicationContext( + "classpath:spring-configuration/rest-config.xml"); + + final BusinessService example = + (BusinessService) test.context.getBean("businessService"); + + example.doSomethingWithCars(); + + example.doSomethingWithCar(66L); + + example.createsNewCar(); + } +} diff --git a/REST/Spring/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/business/service/BusinessService.java b/REST/Spring/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/business/service/BusinessService.java new file mode 100644 index 0000000..ca7a7f2 --- /dev/null +++ b/REST/Spring/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/business/service/BusinessService.java @@ -0,0 +1,11 @@ +package de.spring.webservices.rest.business.service; + + +public interface BusinessService { + + public void doSomethingWithCars(); + + public void doSomethingWithCar(long id); + + public void createsNewCar(); +} diff --git a/REST/Spring/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/business/service/impl/BusinessServiceImpl.java b/REST/Spring/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/business/service/impl/BusinessServiceImpl.java new file mode 100644 index 0000000..b6367e8 --- /dev/null +++ b/REST/Spring/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/business/service/impl/BusinessServiceImpl.java @@ -0,0 +1,53 @@ +package de.spring.webservices.rest.business.service.impl; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import de.spring.webservices.domain.Car; +import de.spring.webservices.rest.business.service.BusinessService; +import de.spring.webservices.rest.client.service.CarClientService; + +@Service("businessService") +public class BusinessServiceImpl implements BusinessService { + private static final Logger LOGGER = LoggerFactory.getLogger(BusinessServiceImpl.class); + + private final CarClientService carClientService; + + @Autowired + public BusinessServiceImpl(CarClientService carClientService) { + this.carClientService = carClientService; + } + + + @Override + public void doSomethingWithCars() { + List cars = carClientService.doGetCars(); + LOGGER.info("Retrieved cars"); + for (Car car : cars) { + LOGGER.info("car: " + car.getId()); + LOGGER.info(car.getContent()); + } + } + + @Override + public void doSomethingWithCar(long id) { + Car car = carClientService.doGetCar(id); + LOGGER.info("Retrieved car"); + LOGGER.info("car: " + car.getId()); + LOGGER.info(car.getContent()); + } + + @Override + public void createsNewCar() { + Car newCar = new Car(666L, "just_a_test"); + + Car car = carClientService.doNewCar(newCar); + LOGGER.info("New car"); + LOGGER.info("car: " + car.getId()); + LOGGER.info(car.getContent()); + } +} diff --git a/REST/Spring/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/client/service/CarClientService.java b/REST/Spring/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/client/service/CarClientService.java new file mode 100644 index 0000000..23966ba --- /dev/null +++ b/REST/Spring/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/client/service/CarClientService.java @@ -0,0 +1,14 @@ +package de.spring.webservices.rest.client.service; + +import java.util.List; + +import de.spring.webservices.domain.Car; + +public interface CarClientService { + + public List doGetCars(); + + public Car doGetCar(long id); + + public Car doNewCar(Car car); +} diff --git a/REST/Spring/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/client/service/impl/CarClientServiceImpl.java b/REST/Spring/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/client/service/impl/CarClientServiceImpl.java new file mode 100644 index 0000000..bdb540b --- /dev/null +++ b/REST/Spring/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/client/service/impl/CarClientServiceImpl.java @@ -0,0 +1,58 @@ +package de.spring.webservices.rest.client.service.impl; + +import java.net.URI; +import java.util.Arrays; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import de.spring.webservices.domain.Car; +import de.spring.webservices.rest.client.service.CarClientService; + +@Service("carClientService") +public class CarClientServiceImpl implements CarClientService { + private static final Logger LOGGER = LoggerFactory.getLogger(CarClientServiceImpl.class); + + private final String apiCarsUrl; + private final String apiCarUrl; + private final RestTemplate restTemplate; + + @Autowired + public CarClientServiceImpl(@Value("${url.base}${url.cars}") String apiCarsUrl, + @Value("${url.base}${url.car}") String apiCarUrl, RestTemplate restTemplate) { + this.apiCarsUrl = apiCarsUrl; + this.apiCarUrl = apiCarUrl; + this.restTemplate = restTemplate; + } + + + @Override + public List doGetCars() { + ResponseEntity responseEntity = restTemplate.getForEntity(apiCarsUrl, Car[].class); + + return Arrays.asList(responseEntity.getBody()); + } + + @Override + public Car doGetCar(long id) { + ResponseEntity responseEntity = restTemplate.getForEntity( + apiCarUrl.replace(":id", String.valueOf(id)), Car.class); + + return responseEntity.getBody(); + } + + @Override + public Car doNewCar(Car car) { + ResponseEntity responseEntity = restTemplate.postForEntity(apiCarsUrl, car, Car.class); + URI newCarLocation = responseEntity.getHeaders().getLocation(); + LOGGER.info("new car location: " + newCarLocation.getPath()); + + return responseEntity.getBody(); + } +} diff --git a/REST/Spring/web-services-spring-rest-client/src/main/resources/log4j2.xml b/REST/Spring/web-services-spring-rest-client/src/main/resources/log4j2.xml new file mode 100644 index 0000000..ee36b97 --- /dev/null +++ b/REST/Spring/web-services-spring-rest-client/src/main/resources/log4j2.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/REST/Spring/web-services-spring-rest-client/src/main/resources/rest.properties b/REST/Spring/web-services-spring-rest-client/src/main/resources/rest.properties new file mode 100644 index 0000000..00255b0 --- /dev/null +++ b/REST/Spring/web-services-spring-rest-client/src/main/resources/rest.properties @@ -0,0 +1,4 @@ +url.base = http://localhost:8080/web-services-spring-rest-server/spring-rest/ + +url.cars = api/cars/ +url.car = api/cars/:id \ No newline at end of file diff --git a/REST/Spring/web-services-spring-rest-client/src/main/resources/spring-configuration/rest-config.xml b/REST/Spring/web-services-spring-rest-client/src/main/resources/spring-configuration/rest-config.xml new file mode 100644 index 0000000..5f4e873 --- /dev/null +++ b/REST/Spring/web-services-spring-rest-client/src/main/resources/spring-configuration/rest-config.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/REST/Spring/web-services-spring-rest-client/src/test/java/de/spring/webservices/rest/business/service/BusinessServiceTest.java b/REST/Spring/web-services-spring-rest-client/src/test/java/de/spring/webservices/rest/business/service/BusinessServiceTest.java new file mode 100644 index 0000000..53665c4 --- /dev/null +++ b/REST/Spring/web-services-spring-rest-client/src/test/java/de/spring/webservices/rest/business/service/BusinessServiceTest.java @@ -0,0 +1,67 @@ +package de.spring.webservices.rest.business.service; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import de.spring.webservices.domain.Car; +import de.spring.webservices.rest.business.service.impl.BusinessServiceImpl; +import de.spring.webservices.rest.client.service.CarClientService; + +public class BusinessServiceTest { + + private CarClientService carClientService; + private BusinessService businessService; + + @Before + public void createTest() { + carClientService = mock(CarClientService.class); + businessService = new BusinessServiceImpl(carClientService); + } + + @Test + public void whenDoSomethingWithCarsThenInvokeDoGetCars() { + Car expectedOne = new Car(66L, "test"); + Car expectedTwo = new Car(99L, "example"); + List expected = new ArrayList<>(); + expected.add(expectedOne); + expected.add(expectedTwo); + when(carClientService.doGetCars()).thenReturn(expected); + + businessService.doSomethingWithCars(); + + verify(carClientService, times(1)).doGetCars(); + } + + @Test + public void whenDoSomethingWithOneCarhenInvokeDoGetCar() { + Long id = 66L; + Car expected = new Car(66L, "test"); + + when(carClientService.doGetCar(id)).thenReturn(expected); + + businessService.doSomethingWithCar(id); + + verify(carClientService, times(1)).doGetCar(id); + } + + @Test + public void whenCreateNewCarThenCreateNewOne() { + Car expected = new Car(66L, "test"); + ArgumentCaptor argCar = ArgumentCaptor.forClass(Car.class); + + when(carClientService.doNewCar(argCar.capture())).thenReturn(expected); + + businessService.createsNewCar(); + + verify(carClientService, times(1)).doNewCar(argCar.getValue()); + } +} diff --git a/REST/Spring/web-services-spring-rest-client/src/test/java/de/spring/webservices/rest/client/service/CarClientServiceIntegrationTest.java b/REST/Spring/web-services-spring-rest-client/src/test/java/de/spring/webservices/rest/client/service/CarClientServiceIntegrationTest.java new file mode 100644 index 0000000..b5696be --- /dev/null +++ b/REST/Spring/web-services-spring-rest-client/src/test/java/de/spring/webservices/rest/client/service/CarClientServiceIntegrationTest.java @@ -0,0 +1,117 @@ +package de.spring.webservices.rest.client.service; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.content; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +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.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.web.client.RestTemplate; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import de.spring.webservices.domain.Car; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath*:spring-configuration/rest-config.xml") +public class CarClientServiceIntegrationTest { + + @Value("${url.base}${url.cars}") + private String apiCarsUrl; + + @Value("${url.base}${url.car}") + private String apiCarUrl; + + @Autowired + private RestTemplate restTemplate; + + @Autowired + private CarClientService carClientService; + + @Autowired + private Jackson2ObjectMapperFactoryBean jsonObjectMapperFactory; + + private MockRestServiceServer mockServer; + + @Before + public void createTest() { + mockServer = MockRestServiceServer.createServer(restTemplate); + } + + @Test + public void whenGetAllCarsThenRetrieveRequestedCars() throws JsonProcessingException { + Car expectedOne = new Car(66L, "test"); + List expected = new ArrayList<>(); + expected.add(expectedOne); + + mockServer.expect(requestTo(apiCarsUrl)) + .andExpect(method(HttpMethod.GET)) + .andRespond(withSuccess(asJsonString(expected), MediaType.APPLICATION_JSON_UTF8)); + + List cars = carClientService.doGetCars(); + + mockServer.verify(); + + assertEquals(1, cars.size()); + assertEquals(expectedOne, cars.get(0)); + } + + @Test + public void whenGetCarByIdThenRetrieveRequestedCar() throws JsonProcessingException { + Long id = 66L; + Car expected = new Car(66L, "test"); + + mockServer.expect(requestTo(apiCarUrl.replace(":id", String.valueOf(id)))) + .andExpect(method(HttpMethod.GET)) + .andRespond(withSuccess(asJsonString(expected), MediaType.APPLICATION_JSON_UTF8)); + + Car car = carClientService.doGetCar(id); + + mockServer.verify(); + + assertNotNull(car); + assertEquals(expected, car); + } + + @Test + public void whenCreateNewCarThenRetrieveCreatedCar() throws JsonProcessingException { + Long expectedId = 66L; + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.LOCATION, "/api/cars/" + String.valueOf(expectedId)); + Car expected = new Car(expectedId, "test"); + + mockServer.expect(requestTo(apiCarsUrl)) + .andExpect(method(HttpMethod.POST)) + .andExpect(content() + .string(asJsonString(expected))) + .andRespond(withSuccess(asJsonString(expected), MediaType.APPLICATION_JSON_UTF8) + .headers(headers)); + + Car car = carClientService.doNewCar(expected); + + mockServer.verify(); + + assertNotNull(car); + assertEquals(expected, car); + } + + private String asJsonString(final Object obj) throws JsonProcessingException { + return jsonObjectMapperFactory.getObject().writeValueAsString(obj); + } +} diff --git a/REST/Spring/web-services-spring-rest-global/pom.xml b/REST/Spring/web-services-spring-rest-global/pom.xml new file mode 100644 index 0000000..c726115 --- /dev/null +++ b/REST/Spring/web-services-spring-rest-global/pom.xml @@ -0,0 +1,12 @@ + + 4.0.0 + + web-services-spring-rest + de.spring.webservices + 1.0-SNAPSHOT + + web-services-spring-rest-global + web-services-spring-rest-global + http://gumartinm.name + diff --git a/REST/Spring/web-services-spring-rest-global/src/main/java/de/spring/webservices/domain/Car.java b/REST/Spring/web-services-spring-rest-global/src/main/java/de/spring/webservices/domain/Car.java new file mode 100644 index 0000000..cdfaa07 --- /dev/null +++ b/REST/Spring/web-services-spring-rest-global/src/main/java/de/spring/webservices/domain/Car.java @@ -0,0 +1,58 @@ +package de.spring.webservices.domain; + +public class Car { + + private final Long id; + private final String content; + + // Required by Jackson :/ + public Car() { + this.id = null; + this.content = null; + } + + public Car(Long id, String content) { + this.id = id; + this.content = content; + } + + + public Long getId() { + return id; + } + + public String getContent() { + return content; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((content == null) ? 0 : content.hashCode()); + result = prime * result + ((id == null) ? 0 : id.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Car other = (Car) obj; + if (content == null) { + if (other.content != null) + return false; + } else if (!content.equals(other.content)) + return false; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + return true; + } +} diff --git a/REST/Spring/web-services-spring-rest-server/pom.xml b/REST/Spring/web-services-spring-rest-server/pom.xml new file mode 100644 index 0000000..144a67c --- /dev/null +++ b/REST/Spring/web-services-spring-rest-server/pom.xml @@ -0,0 +1,139 @@ + + 4.0.0 + + web-services-spring-rest + de.spring.webservices + 1.0-SNAPSHOT + + web-services-spring-rest-server + war + web-services-spring-rest-server + http://gumartinm.name + + + de.spring.webservices + web-services-spring-rest-global + 1.0-SNAPSHOT + + + + org.springframework + spring-core + + + org.springframework + spring-webmvc + + + + + javax.servlet + javax.servlet-api + provided + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + org.jvnet.ws.wadl + wadl-core + 1.1.6 + + + org.jvnet.ws.wadl + wadl-client-plugin + 1.1.6 + + + org.springframework + spring-oxm + 4.2.4.RELEASE + + + + + io.springfox + springfox-swagger1 + 2.3.1 + + + io.springfox + springfox-swagger-ui + 2.3.1 + + + + + javax.validation + validation-api + + + org.hibernate + hibernate-validator + + + + + junit + junit + test + + + org.springframework + spring-test + test + + + org.mockito + mockito-core + test + + + + + org.hamcrest + hamcrest-core + test + + + org.hamcrest + hamcrest-library + test + + + com.jayway.jsonpath + json-path + test + + + + ${project.artifactId} + + + ${basedir}/src/main/webapp + + **/*.* + + + + ${basedir}/src/main/resources/ + + **/*.* + + + + + + + org.apache.maven.plugins + maven-war-plugin + + + + diff --git a/REST/Spring/web-services-spring-rest-server/src/main/java/de/spring/webservices/doc/Swagger2Configuration.java b/REST/Spring/web-services-spring-rest-server/src/main/java/de/spring/webservices/doc/Swagger2Configuration.java new file mode 100644 index 0000000..df71590 --- /dev/null +++ b/REST/Spring/web-services-spring-rest-server/src/main/java/de/spring/webservices/doc/Swagger2Configuration.java @@ -0,0 +1,58 @@ +package de.spring.webservices.doc; + +import static com.google.common.collect.Lists.newArrayList; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.builders.ResponseMessageBuilder; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger.web.UiConfiguration; +import springfox.documentation.swagger1.annotations.EnableSwagger; + +@Configuration +@EnableWebMvc +@EnableSwagger +@ComponentScan("de.spring.webservices.rest.controller") +public class Swagger2Configuration { + + @Bean + public Docket documentation() { + return new Docket(DocumentationType.SWAGGER_12) + .select() + .apis(RequestHandlerSelectors.withMethodAnnotation(RequestMapping.class)) + .paths(PathSelectors.any()) + .build() + .globalResponseMessage(RequestMethod.GET, + newArrayList(new ResponseMessageBuilder() + .code(500).message("Global server custom error message").build())) + .pathMapping("/") + .useDefaultResponseMessages(false) + .apiInfo(metadata()) + .enable(true); + } + + @Bean + UiConfiguration uiConfig() { + return UiConfiguration.DEFAULT; + } + + private static ApiInfo metadata() { + return new ApiInfoBuilder() + .title("gumartinm REST API") + .description("Gustavo Martin Morcuende") + .version("1.0-SNAPSHOT") + .contact("gumartinm.name") + .build(); + } + +} diff --git a/REST/Spring/web-services-spring-rest-server/src/main/java/de/spring/webservices/rest/controller/CarController.java b/REST/Spring/web-services-spring-rest-server/src/main/java/de/spring/webservices/rest/controller/CarController.java new file mode 100644 index 0000000..49acf64 --- /dev/null +++ b/REST/Spring/web-services-spring-rest-server/src/main/java/de/spring/webservices/rest/controller/CarController.java @@ -0,0 +1,112 @@ +package de.spring.webservices.rest.controller; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +import de.spring.webservices.domain.Car; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import io.swagger.annotations.ResponseHeader; + +@RestController +@RequestMapping("/api/cars/") +public class CarController { + private static final Logger LOGGER = LoggerFactory.getLogger(CarController.class); + private static final String TEMPLATE = "Car: %s"; + + private final AtomicLong counter = new AtomicLong(); + + @ApiOperation(value = "Get all available cars", nickname = "getAllCars", responseContainer="List", response = Car.class) + @ApiResponses({ + @ApiResponse(code = 404, message ="Specific getCars not found"), + @ApiResponse(code = 400, message ="Specific getCars invalid input") + }) + @RequestMapping(produces = { MediaType.APPLICATION_JSON_UTF8_VALUE }, method = RequestMethod.GET) + @ResponseStatus(HttpStatus.OK) + public List cars() { + final List cars = new ArrayList<>(); + cars.add(new Car(counter.incrementAndGet(), String.format(TEMPLATE, 1))); + cars.add(new Car(counter.incrementAndGet(), String.format(TEMPLATE, 2))); + cars.add(new Car(counter.incrementAndGet(), String.format(TEMPLATE, 3))); + + return cars; + } + + @ApiOperation(value = "Get one car", nickname = "getOneCar", response = Car.class) + @ApiResponses({ + @ApiResponse(code = 404, message ="Specific getCar not found"), + @ApiResponse(code = 400, message ="Specific getCar invalid input") + }) + @RequestMapping(value = "{id}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.GET) + @ResponseStatus(HttpStatus.OK) + public Car car(@RequestHeader(value = "MY_HEADER", required = false) String specialHeader, + @ApiParam(name = "id", value = "Car id", required = true) @PathVariable("id") long id, + @RequestParam Map params, + @RequestParam(value = "wheel", required = false) String[] wheelParams) { + + if (specialHeader != null) { + LOGGER.info("SPECIAL HEADER: " + specialHeader); + } + + if (params.get("mirror") != null) { + LOGGER.info("MIRROR: " + params.get("mirror")); + } + + if (params.get("window") != null) { + LOGGER.info("WINDOW: " + params.get("window")); + } + + if (wheelParams != null) { + for(String wheel : wheelParams) { + LOGGER.info(wheel); + } + } + + try { + Thread.sleep(10000); //1000 milliseconds is one second. + } catch(InterruptedException ex) { + Thread.currentThread().interrupt(); + } + + + return new Car(counter.incrementAndGet(), String.format(TEMPLATE, id)); + } + + @ApiOperation(code = 201, value = "Create one new car", nickname = "createNewCar") + @ApiResponses({ + @ApiResponse(code = 201, message ="Specific createCar with header", + responseHeaders = { @ResponseHeader(name = HttpHeaders.LOCATION) }, response = Car.class), + @ApiResponse(code = 404, message ="Specific createCar not found"), + @ApiResponse(code = 400, message ="Specific createCar invalid input") + }) + @RequestMapping(consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, + produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.POST) + @ResponseStatus(HttpStatus.CREATED) + public ResponseEntity create(@RequestBody Car car) { + long count = counter.incrementAndGet(); + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.LOCATION, "/api/cars/" + count); + + return new ResponseEntity<>(new Car(count, String.format(TEMPLATE, count)), headers, HttpStatus.CREATED); + } + +} diff --git a/REST/Spring/web-services-spring-rest-server/src/main/java/de/spring/webservices/rest/wadl/WADLController.java b/REST/Spring/web-services-spring-rest-server/src/main/java/de/spring/webservices/rest/wadl/WADLController.java new file mode 100644 index 0000000..1125157 --- /dev/null +++ b/REST/Spring/web-services-spring-rest-server/src/main/java/de/spring/webservices/rest/wadl/WADLController.java @@ -0,0 +1,236 @@ +package de.spring.webservices.rest.wadl; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; +import javax.xml.namespace.QName; + +import org.apache.commons.lang.StringUtils; +import org.jvnet.ws.wadl.Application; +import org.jvnet.ws.wadl.Doc; +import org.jvnet.ws.wadl.Param; +import org.jvnet.ws.wadl.ParamStyle; +import org.jvnet.ws.wadl.Representation; +import org.jvnet.ws.wadl.Request; +import org.jvnet.ws.wadl.Resource; +import org.jvnet.ws.wadl.Resources; +import org.jvnet.ws.wadl.Response; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.ValueConstants; +import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; + +/** + * Taken from: http://javattitude.com/2014/05/26/wadl-generator-for-spring-rest/ + * + * With some modifications. + * + */ +@Controller +@RequestMapping("/rest.wadl") +public class WADLController { + private static final String NAMESPACE_URI = "http://www.w3.org/2001/XMLSchema" ; + private static final String WADL_TITLE = "Spring REST Service WADL"; + + private final AbstractHandlerMethodMapping handlerMapping; + private final ApplicationContext context; + + @Autowired + public WADLController(AbstractHandlerMethodMapping handlerMapping, ApplicationContext context) { + this.handlerMapping = handlerMapping; + this.context = context; + } + + @RequestMapping(produces = { MediaType.APPLICATION_XML_VALUE }, method=RequestMethod.GET ) + public @ResponseBody Application generateWadl(HttpServletRequest request) { + Application result = new Application(); + + Doc doc = new Doc(); + doc.setTitle(WADL_TITLE); + result.getDoc().add(doc); + + Resources wadlResources = new Resources(); + wadlResources.setBase(getBaseUrl(request)); + + handlerMapping.getHandlerMethods().forEach( (mappingInfo, handlerMethod) -> { + Object object = handlerMethod.getBean(); + Object bean = context.getBean(object.toString()); + if(!bean.getClass().isAnnotationPresent(RestController.class)) { + return; + } + + mappingInfo.getMethodsCondition().getMethods().forEach(httpMethod -> { + Resource wadlResource = null; + org.jvnet.ws.wadl.Method wadlMethod = new org.jvnet.ws.wadl.Method(); + + Set pattern = mappingInfo.getPatternsCondition().getPatterns(); + for (String uri : pattern) { + wadlResource = createOrFind(uri, wadlResources); + wadlResource.setPath(uri); + } + + wadlMethod.setName(httpMethod.name()); + Method javaMethod = handlerMethod.getMethod(); + wadlMethod.setId(javaMethod.getName()); + Doc wadlDocMethod = new Doc(); + wadlDocMethod.setTitle(javaMethod.getDeclaringClass().getSimpleName() + "." + javaMethod.getName()); + wadlMethod.getDoc().add(wadlDocMethod); + + // Request + Request wadlRequest = new Request(); + Annotation[][] annotations = javaMethod.getParameterAnnotations(); + Class[] paramTypes = javaMethod.getParameterTypes(); + int i = 0; + for (Annotation[] annotation : annotations) { + Class paramType =paramTypes[i]; + i++; + for (Annotation annotation2 : annotation) { + + Param wadlParam = doParam(annotation2, paramType); + if (wadlParam != null) { + wadlRequest.getParam().add(wadlParam); + } + } + } + if (!wadlRequest.getParam().isEmpty() ) { + wadlMethod.setRequest(wadlRequest); + } + + // Response + Set mediaTypes = mappingInfo.getProducesCondition().getProducibleMediaTypes(); + if (!mediaTypes.isEmpty()) { + ResponseStatus status = handlerMethod.getMethodAnnotation(ResponseStatus.class); + Response wadlResponse = doResponse(mediaTypes, status); + wadlMethod.getResponse().add(wadlResponse); + } + + + wadlResource.getMethodOrResource().add(wadlMethod); + }); + + }); + result.getResources().add(wadlResources); + + return result; + } + + private Param doParam(Annotation annotation2, Class paramType) { + Param wadlParam = null; + + if (annotation2 instanceof RequestParam ) { + RequestParam param = (RequestParam)annotation2; + + wadlParam = new Param(); + QName nm = convertJavaToXMLType(paramType); + if (StringUtils.isNotEmpty(nm.getLocalPart())) { + wadlParam.setType(nm); + } + wadlParam.setName(param.value()); + + + if (!ValueConstants.DEFAULT_NONE.equals(param.defaultValue())) { + String defaultValue = cleanDefault(param.defaultValue()); + if (StringUtils.isNotEmpty(defaultValue) ) { + wadlParam.setDefault(defaultValue); + } + } + + wadlParam.setStyle(ParamStyle.QUERY); + wadlParam.setRequired(param.required()); + } else if (annotation2 instanceof PathVariable ) { + PathVariable param = (PathVariable)annotation2; + + wadlParam = new Param(); + QName nm = convertJavaToXMLType(paramType); + if (StringUtils.isNotEmpty(nm.getLocalPart())) { + wadlParam.setType(nm); + } + wadlParam.setName(param.value()); + + + wadlParam.setStyle(ParamStyle.TEMPLATE); + wadlParam.setRequired(true); + } + + return wadlParam; + } + + private Response doResponse(Set mediaTypes, ResponseStatus status) { + Response wadlResponse = new Response(); + + mediaTypes.forEach(mediaType -> { + Representation wadlRepresentation = new Representation(); + wadlRepresentation.setMediaType(mediaType.toString()); + wadlResponse.getRepresentation().add(wadlRepresentation); + }); + + wadlResponse.getStatus().add(getResposeValue(status)); + + return wadlResponse; + } + + private long getResposeValue(ResponseStatus status) { + if(status == null) { + return HttpStatus.OK.value(); + } else { + HttpStatus httpcode = status.value(); + return httpcode.value(); + } + } + + private QName convertJavaToXMLType(Class type) { + QName nm = new QName(""); + String classname = type.toString(); + classname = classname.toLowerCase(); + + if (classname.indexOf("string") >= 0) { + nm = new QName(NAMESPACE_URI, "string", "xs"); + } else if (classname.indexOf("integer") >= 0) { + nm = new QName(NAMESPACE_URI, "int", "xs"); + } else if (classname.indexOf("long") >= 0) { + nm = new QName(NAMESPACE_URI, "long", "xs"); + } + + return nm; + } + + private Resource createOrFind(String uri, Resources wadResources) { + List current = wadResources.getResource(); + for(Resource resource:current) { + if(resource.getPath().equalsIgnoreCase(uri)){ + return resource; + } + } + Resource wadlResource = new Resource(); + current.add(wadlResource); + return wadlResource; + } + + private String getBaseUrl(HttpServletRequest request) { + String requestUri = request.getRequestURI(); + int index = requestUri.lastIndexOf('/'); + requestUri = requestUri.substring(0, index); + + return request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + requestUri; + } + + private String cleanDefault(String value) { + value = value.replaceAll("\t", ""); + value = value.replaceAll("\n", ""); + return value; + } +} diff --git a/REST/Spring/web-services-spring-rest-server/src/main/resources/log4j2.xml b/REST/Spring/web-services-spring-rest-server/src/main/resources/log4j2.xml new file mode 100644 index 0000000..ee36b97 --- /dev/null +++ b/REST/Spring/web-services-spring-rest-server/src/main/resources/log4j2.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/REST/Spring/web-services-spring-rest-server/src/main/resources/spring-configuration/mvc/rest/rest-config.xml b/REST/Spring/web-services-spring-rest-server/src/main/resources/spring-configuration/mvc/rest/rest-config.xml new file mode 100644 index 0000000..54b8540 --- /dev/null +++ b/REST/Spring/web-services-spring-rest-server/src/main/resources/spring-configuration/mvc/rest/rest-config.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/REST/Spring/web-services-spring-rest-server/src/main/resources/spring-configuration/spring-config.xml b/REST/Spring/web-services-spring-rest-server/src/main/resources/spring-configuration/spring-config.xml new file mode 100644 index 0000000..e50129b --- /dev/null +++ b/REST/Spring/web-services-spring-rest-server/src/main/resources/spring-configuration/spring-config.xml @@ -0,0 +1,13 @@ + + + + diff --git a/REST/Spring/web-services-spring-rest-server/src/main/resources/spring-configuration/spring-doc-config.xml b/REST/Spring/web-services-spring-rest-server/src/main/resources/spring-configuration/spring-doc-config.xml new file mode 100644 index 0000000..773e14f --- /dev/null +++ b/REST/Spring/web-services-spring-rest-server/src/main/resources/spring-configuration/spring-doc-config.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + diff --git a/REST/Spring/web-services-spring-rest-server/src/main/webapp/WEB-INF/web.xml b/REST/Spring/web-services-spring-rest-server/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..6c9c1d0 --- /dev/null +++ b/REST/Spring/web-services-spring-rest-server/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,41 @@ + + + + Spring REST Services: example + + + + org.springframework.web.context.ContextLoaderListener + + + + + spring.profiles.active + ${environment.profile} + contextConfigLocation + + classpath*:spring-configuration/*.xml + + + + + + spring-rest + org.springframework.web.servlet.DispatcherServlet + 1 + true + + contextConfigLocation + classpath*:spring-configuration/mvc/rest/*.xml + + + + + spring-rest + + /* + + + diff --git a/REST/Spring/web-services-spring-rest-server/src/test/java/de/spring/webservices/rest/controller/CarControllerIntegrationTest.java b/REST/Spring/web-services-spring-rest-server/src/test/java/de/spring/webservices/rest/controller/CarControllerIntegrationTest.java new file mode 100644 index 0000000..c856efb --- /dev/null +++ b/REST/Spring/web-services-spring-rest-server/src/test/java/de/spring/webservices/rest/controller/CarControllerIntegrationTest.java @@ -0,0 +1,85 @@ +package de.spring.webservices.rest.controller; + +import static org.hamcrest.Matchers.any; +import static org.hamcrest.Matchers.is; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import de.spring.webservices.domain.Car; + + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration({ "classpath*:spring-configuration/mvc/rest/*.xml"}) +public class CarControllerIntegrationTest { + private CarController controller; + private MockMvc mockMvc; + + @Before + public void setup() { + controller = new CarController(); + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Test + public void testWhenGetAllCarsThenRetrieveJsonValues() throws Exception { + mockMvc.perform(get("/api/cars/") + .accept(MediaType.APPLICATION_JSON_UTF8)) + + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].id", any(Integer.class))) + .andExpect(jsonPath("$[0].content", is("Car: 1"))) + .andExpect(jsonPath("$[1].content", is("Car: 2"))) + .andExpect(jsonPath("$[1].id", any(Integer.class))) + .andExpect(jsonPath("$[2].content", is("Car: 3"))) + .andExpect(jsonPath("$[2].id", any(Integer.class))) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)); + } + + @Test + public void testWhenGetOneCarThenRetrieveJsonValue() throws Exception { + mockMvc.perform(get("/api/cars/{id}", 1L) + .accept(MediaType.APPLICATION_JSON_UTF8)) + + .andExpect(status().isOk()) + .andExpect(jsonPath("id", any(Integer.class))) + .andExpect(jsonPath("content", is("Car: 1"))) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)); + } + + @Test + public void testWhenCreateNewCarThenRetrieveJsonValue() throws Exception { + Car car = new Car(2L, "nothing"); + mockMvc.perform(post("/api/cars/") + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content(asJsonString(car)) + .accept(MediaType.APPLICATION_JSON_UTF8)) + + .andExpect(status().isCreated()) + .andExpect(jsonPath("id", any(Integer.class))) + .andExpect(jsonPath("content", is("Car: 1"))) + .andExpect(header().string(HttpHeaders.LOCATION, "/api/cars/1")) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)); + } + + private static String asJsonString(final Object obj) throws JsonProcessingException { + final ObjectMapper mapper = new ObjectMapper(); + return mapper.writeValueAsString(obj); + } +} diff --git a/REST/Spring/web-services-spring-rest/pom.xml b/REST/Spring/web-services-spring-rest/pom.xml new file mode 100644 index 0000000..e8ce3dc --- /dev/null +++ b/REST/Spring/web-services-spring-rest/pom.xml @@ -0,0 +1,265 @@ + + 4.0.0 + de.spring.webservices + web-services-spring-rest + pom + 1.0-SNAPSHOT + web-services-spring-rest + http://gumartinm.name + Web Services Spring Framework + + Gustavo Martin Morcuende + http://www.gumartinm.name + + + scm:git:http://git.gumartinm.name/SpringWebServicesForFun + http://git.gumartinm.name/SpringWebServicesForFun + + + UTF-8 + UTF-8 + 4.2.4.RELEASE + + + + release + + release + + + true + + + + + + + org.apache.logging.log4j + log4j-slf4j-impl + 2.3 + + + + + org.apache.logging.log4j + log4j-core + 2.3 + + + + + org.slf4j + jcl-over-slf4j + 1.7.12 + + + + + cglib + cglib + 2.2.2 + + + + + + org.springframework + spring-core + ${spring.version} + + + + commons-logging + commons-logging + + + + + org.springframework + spring-webmvc + ${spring.version} + + + + commons-logging + commons-logging + + + + + + + javax.servlet + javax.servlet-api + 4.0.0-b01 + provided + + + + + com.fasterxml.jackson.core + jackson-databind + 2.6.4 + + + + + + javax.validation + validation-api + 1.1.0.Final + + + org.hibernate + hibernate-validator + 5.2.2.Final + + + + + junit + junit + 4.12 + test + + + org.springframework + spring-test + ${spring.version} + test + + + org.mockito + mockito-core + 2.0.11-beta + test + + + + + org.hamcrest + hamcrest-core + 1.3 + test + + + org.hamcrest + hamcrest-library + 1.3 + test + + + com.jayway.jsonpath + json-path + 2.1.0 + test + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18.1 + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.18.1 + + + org.apache.maven.plugins + maven-war-plugin + 2.6 + + + + true + src/main/webapp + + WEB-INF/web.xml + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.3 + + 1.8 + 1.8 + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-resources-plugin + 2.7 + + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + ${project.description} + ${project.version} + ${project.organization.name} + ${project.description} + ${project.version} + ${project.organization.name} + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*IntegrationTest.java + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + + + **/*IntegrationTest.java + + + + + + \ No newline at end of file diff --git a/REST/SpringBoot/pom.xml b/REST/SpringBoot/pom.xml new file mode 100644 index 0000000..9b7f623 --- /dev/null +++ b/REST/SpringBoot/pom.xml @@ -0,0 +1,27 @@ + + 4.0.0 + de.example.spring.kafka + web-services-spring-rest + 1.0-SNAPSHOT + pom + web-services-spring-rest + https://gumartinm.name/ + Web Services Spring REST + + gumartinm + https://gumartinm.name/ + + + scm:git:https://git.gumartinm.name/SpringWebServicesForFun + https://git.gumartinm.name/SpringWebServicesForFun + + + + web-services-spring-rest-bom + web-services-spring-rest-global + web-services-spring-rest-server + + + + diff --git a/REST/SpringBoot/web-services-spring-rest-bom/pom.xml b/REST/SpringBoot/web-services-spring-rest-bom/pom.xml new file mode 100644 index 0000000..2e5ab89 --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-bom/pom.xml @@ -0,0 +1,159 @@ + + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 1.5.1.RELEASE + + + de.spring.webservices + web-services-spring-rest-bom + 1.0-SNAPSHOT + pom + web-services-spring-rest-bom + + https://gumartinm.name + Web Services Spring REST Framework BOM. + + gumartinm + https://gumartinm.name + + + scm:git:https://git.gumartinm.name/SpringWebServicesForFun + https://git.gumartinm.name/SpringWebServicesForFun + + + false + false + UTF-8 + + + + release + + release + + + true + + + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-log4j2 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-resources-plugin + + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-jar-plugin + + + + ${project.description} + ${project.version} + ${project.organization.name} + ${project.description} + ${project.version} + ${project.organization.name} + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${skip.unit.tests} + + + **/*IT.java + **/*IntegrationTest.java + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + ${skip.integration.tests} + + **/*IT.java + **/*IntegrationTest.java + + + + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + 1.4.4.RELEASE + + + + + diff --git a/REST/SpringBoot/web-services-spring-rest-client/pom.xml b/REST/SpringBoot/web-services-spring-rest-client/pom.xml new file mode 100644 index 0000000..276b3db --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-client/pom.xml @@ -0,0 +1,61 @@ + + 4.0.0 + + web-services-spring-rest + de.spring.webservices + 1.0-SNAPSHOT + + web-services-spring-rest-client + web-services-spring-rest-client + http://gumartinm.name + + + de.spring.webservices + web-services-spring-rest-global + 1.0-SNAPSHOT + + + + org.springframework + spring-core + + + org.springframework + spring-webmvc + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + junit + junit + test + + + org.springframework + spring-test + test + + + org.mockito + mockito-core + test + + + + + + ${basedir}/src/main/resources/ + + **/*.* + + + + + \ No newline at end of file diff --git a/REST/SpringBoot/web-services-spring-rest-client/src/main/java/de/spring/webservices/main/MainTest.java b/REST/SpringBoot/web-services-spring-rest-client/src/main/java/de/spring/webservices/main/MainTest.java new file mode 100644 index 0000000..0f066ad --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-client/src/main/java/de/spring/webservices/main/MainTest.java @@ -0,0 +1,35 @@ +package de.spring.webservices.main; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +import de.spring.webservices.rest.business.service.BusinessService; + + +/** + * This class is used just like a nice example about how to write and run client + * code which will send data to and from the Web Services. + * + */ +public class MainTest { + public ApplicationContext context; + + /** + * @param args + */ + public static void main(final String[] args) { + final MainTest test = new MainTest(); + + test.context = new ClassPathXmlApplicationContext( + "classpath:spring-configuration/rest-config.xml"); + + final BusinessService example = + (BusinessService) test.context.getBean("businessService"); + + example.doSomethingWithCars(); + + example.doSomethingWithCar(66L); + + example.createsNewCar(); + } +} diff --git a/REST/SpringBoot/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/business/service/BusinessService.java b/REST/SpringBoot/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/business/service/BusinessService.java new file mode 100644 index 0000000..ca7a7f2 --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/business/service/BusinessService.java @@ -0,0 +1,11 @@ +package de.spring.webservices.rest.business.service; + + +public interface BusinessService { + + public void doSomethingWithCars(); + + public void doSomethingWithCar(long id); + + public void createsNewCar(); +} diff --git a/REST/SpringBoot/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/business/service/impl/BusinessServiceImpl.java b/REST/SpringBoot/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/business/service/impl/BusinessServiceImpl.java new file mode 100644 index 0000000..b6367e8 --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/business/service/impl/BusinessServiceImpl.java @@ -0,0 +1,53 @@ +package de.spring.webservices.rest.business.service.impl; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import de.spring.webservices.domain.Car; +import de.spring.webservices.rest.business.service.BusinessService; +import de.spring.webservices.rest.client.service.CarClientService; + +@Service("businessService") +public class BusinessServiceImpl implements BusinessService { + private static final Logger LOGGER = LoggerFactory.getLogger(BusinessServiceImpl.class); + + private final CarClientService carClientService; + + @Autowired + public BusinessServiceImpl(CarClientService carClientService) { + this.carClientService = carClientService; + } + + + @Override + public void doSomethingWithCars() { + List cars = carClientService.doGetCars(); + LOGGER.info("Retrieved cars"); + for (Car car : cars) { + LOGGER.info("car: " + car.getId()); + LOGGER.info(car.getContent()); + } + } + + @Override + public void doSomethingWithCar(long id) { + Car car = carClientService.doGetCar(id); + LOGGER.info("Retrieved car"); + LOGGER.info("car: " + car.getId()); + LOGGER.info(car.getContent()); + } + + @Override + public void createsNewCar() { + Car newCar = new Car(666L, "just_a_test"); + + Car car = carClientService.doNewCar(newCar); + LOGGER.info("New car"); + LOGGER.info("car: " + car.getId()); + LOGGER.info(car.getContent()); + } +} diff --git a/REST/SpringBoot/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/client/service/CarClientService.java b/REST/SpringBoot/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/client/service/CarClientService.java new file mode 100644 index 0000000..23966ba --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/client/service/CarClientService.java @@ -0,0 +1,14 @@ +package de.spring.webservices.rest.client.service; + +import java.util.List; + +import de.spring.webservices.domain.Car; + +public interface CarClientService { + + public List doGetCars(); + + public Car doGetCar(long id); + + public Car doNewCar(Car car); +} diff --git a/REST/SpringBoot/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/client/service/impl/CarClientServiceImpl.java b/REST/SpringBoot/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/client/service/impl/CarClientServiceImpl.java new file mode 100644 index 0000000..bdb540b --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/client/service/impl/CarClientServiceImpl.java @@ -0,0 +1,58 @@ +package de.spring.webservices.rest.client.service.impl; + +import java.net.URI; +import java.util.Arrays; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import de.spring.webservices.domain.Car; +import de.spring.webservices.rest.client.service.CarClientService; + +@Service("carClientService") +public class CarClientServiceImpl implements CarClientService { + private static final Logger LOGGER = LoggerFactory.getLogger(CarClientServiceImpl.class); + + private final String apiCarsUrl; + private final String apiCarUrl; + private final RestTemplate restTemplate; + + @Autowired + public CarClientServiceImpl(@Value("${url.base}${url.cars}") String apiCarsUrl, + @Value("${url.base}${url.car}") String apiCarUrl, RestTemplate restTemplate) { + this.apiCarsUrl = apiCarsUrl; + this.apiCarUrl = apiCarUrl; + this.restTemplate = restTemplate; + } + + + @Override + public List doGetCars() { + ResponseEntity responseEntity = restTemplate.getForEntity(apiCarsUrl, Car[].class); + + return Arrays.asList(responseEntity.getBody()); + } + + @Override + public Car doGetCar(long id) { + ResponseEntity responseEntity = restTemplate.getForEntity( + apiCarUrl.replace(":id", String.valueOf(id)), Car.class); + + return responseEntity.getBody(); + } + + @Override + public Car doNewCar(Car car) { + ResponseEntity responseEntity = restTemplate.postForEntity(apiCarsUrl, car, Car.class); + URI newCarLocation = responseEntity.getHeaders().getLocation(); + LOGGER.info("new car location: " + newCarLocation.getPath()); + + return responseEntity.getBody(); + } +} diff --git a/REST/SpringBoot/web-services-spring-rest-client/src/main/resources/log4j2.xml b/REST/SpringBoot/web-services-spring-rest-client/src/main/resources/log4j2.xml new file mode 100644 index 0000000..ee36b97 --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-client/src/main/resources/log4j2.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/REST/SpringBoot/web-services-spring-rest-client/src/main/resources/rest.properties b/REST/SpringBoot/web-services-spring-rest-client/src/main/resources/rest.properties new file mode 100644 index 0000000..00255b0 --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-client/src/main/resources/rest.properties @@ -0,0 +1,4 @@ +url.base = http://localhost:8080/web-services-spring-rest-server/spring-rest/ + +url.cars = api/cars/ +url.car = api/cars/:id \ No newline at end of file diff --git a/REST/SpringBoot/web-services-spring-rest-client/src/main/resources/spring-configuration/rest-config.xml b/REST/SpringBoot/web-services-spring-rest-client/src/main/resources/spring-configuration/rest-config.xml new file mode 100644 index 0000000..5f4e873 --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-client/src/main/resources/spring-configuration/rest-config.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/REST/SpringBoot/web-services-spring-rest-client/src/test/java/de/spring/webservices/rest/business/service/BusinessServiceTest.java b/REST/SpringBoot/web-services-spring-rest-client/src/test/java/de/spring/webservices/rest/business/service/BusinessServiceTest.java new file mode 100644 index 0000000..53665c4 --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-client/src/test/java/de/spring/webservices/rest/business/service/BusinessServiceTest.java @@ -0,0 +1,67 @@ +package de.spring.webservices.rest.business.service; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import de.spring.webservices.domain.Car; +import de.spring.webservices.rest.business.service.impl.BusinessServiceImpl; +import de.spring.webservices.rest.client.service.CarClientService; + +public class BusinessServiceTest { + + private CarClientService carClientService; + private BusinessService businessService; + + @Before + public void createTest() { + carClientService = mock(CarClientService.class); + businessService = new BusinessServiceImpl(carClientService); + } + + @Test + public void whenDoSomethingWithCarsThenInvokeDoGetCars() { + Car expectedOne = new Car(66L, "test"); + Car expectedTwo = new Car(99L, "example"); + List expected = new ArrayList<>(); + expected.add(expectedOne); + expected.add(expectedTwo); + when(carClientService.doGetCars()).thenReturn(expected); + + businessService.doSomethingWithCars(); + + verify(carClientService, times(1)).doGetCars(); + } + + @Test + public void whenDoSomethingWithOneCarhenInvokeDoGetCar() { + Long id = 66L; + Car expected = new Car(66L, "test"); + + when(carClientService.doGetCar(id)).thenReturn(expected); + + businessService.doSomethingWithCar(id); + + verify(carClientService, times(1)).doGetCar(id); + } + + @Test + public void whenCreateNewCarThenCreateNewOne() { + Car expected = new Car(66L, "test"); + ArgumentCaptor argCar = ArgumentCaptor.forClass(Car.class); + + when(carClientService.doNewCar(argCar.capture())).thenReturn(expected); + + businessService.createsNewCar(); + + verify(carClientService, times(1)).doNewCar(argCar.getValue()); + } +} diff --git a/REST/SpringBoot/web-services-spring-rest-client/src/test/java/de/spring/webservices/rest/client/service/CarClientServiceIntegrationTest.java b/REST/SpringBoot/web-services-spring-rest-client/src/test/java/de/spring/webservices/rest/client/service/CarClientServiceIntegrationTest.java new file mode 100644 index 0000000..b5696be --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-client/src/test/java/de/spring/webservices/rest/client/service/CarClientServiceIntegrationTest.java @@ -0,0 +1,117 @@ +package de.spring.webservices.rest.client.service; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.content; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +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.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.web.client.RestTemplate; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import de.spring.webservices.domain.Car; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath*:spring-configuration/rest-config.xml") +public class CarClientServiceIntegrationTest { + + @Value("${url.base}${url.cars}") + private String apiCarsUrl; + + @Value("${url.base}${url.car}") + private String apiCarUrl; + + @Autowired + private RestTemplate restTemplate; + + @Autowired + private CarClientService carClientService; + + @Autowired + private Jackson2ObjectMapperFactoryBean jsonObjectMapperFactory; + + private MockRestServiceServer mockServer; + + @Before + public void createTest() { + mockServer = MockRestServiceServer.createServer(restTemplate); + } + + @Test + public void whenGetAllCarsThenRetrieveRequestedCars() throws JsonProcessingException { + Car expectedOne = new Car(66L, "test"); + List expected = new ArrayList<>(); + expected.add(expectedOne); + + mockServer.expect(requestTo(apiCarsUrl)) + .andExpect(method(HttpMethod.GET)) + .andRespond(withSuccess(asJsonString(expected), MediaType.APPLICATION_JSON_UTF8)); + + List cars = carClientService.doGetCars(); + + mockServer.verify(); + + assertEquals(1, cars.size()); + assertEquals(expectedOne, cars.get(0)); + } + + @Test + public void whenGetCarByIdThenRetrieveRequestedCar() throws JsonProcessingException { + Long id = 66L; + Car expected = new Car(66L, "test"); + + mockServer.expect(requestTo(apiCarUrl.replace(":id", String.valueOf(id)))) + .andExpect(method(HttpMethod.GET)) + .andRespond(withSuccess(asJsonString(expected), MediaType.APPLICATION_JSON_UTF8)); + + Car car = carClientService.doGetCar(id); + + mockServer.verify(); + + assertNotNull(car); + assertEquals(expected, car); + } + + @Test + public void whenCreateNewCarThenRetrieveCreatedCar() throws JsonProcessingException { + Long expectedId = 66L; + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.LOCATION, "/api/cars/" + String.valueOf(expectedId)); + Car expected = new Car(expectedId, "test"); + + mockServer.expect(requestTo(apiCarsUrl)) + .andExpect(method(HttpMethod.POST)) + .andExpect(content() + .string(asJsonString(expected))) + .andRespond(withSuccess(asJsonString(expected), MediaType.APPLICATION_JSON_UTF8) + .headers(headers)); + + Car car = carClientService.doNewCar(expected); + + mockServer.verify(); + + assertNotNull(car); + assertEquals(expected, car); + } + + private String asJsonString(final Object obj) throws JsonProcessingException { + return jsonObjectMapperFactory.getObject().writeValueAsString(obj); + } +} diff --git a/REST/SpringBoot/web-services-spring-rest-global/pom.xml b/REST/SpringBoot/web-services-spring-rest-global/pom.xml new file mode 100644 index 0000000..744c1e3 --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-global/pom.xml @@ -0,0 +1,31 @@ + + 4.0.0 + + web-services-spring-rest-bom + de.spring.webservices + 1.0-SNAPSHOT + + web-services-spring-rest-global + web-services-spring-rest-global + http://gumartinm.name + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + diff --git a/REST/SpringBoot/web-services-spring-rest-global/src/main/java/de/spring/webservices/domain/Car.java b/REST/SpringBoot/web-services-spring-rest-global/src/main/java/de/spring/webservices/domain/Car.java new file mode 100644 index 0000000..d062776 --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-global/src/main/java/de/spring/webservices/domain/Car.java @@ -0,0 +1,52 @@ +package de.spring.webservices.domain; + +import java.util.Objects; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +public class Car { + + @NotNull + private Long id; + + @NotNull + @Size(max = 1024) + private String content; + + // Required by Jackson :/ + protected Car() { + + } + + public Car(Long id, String content) { + this.id = id; + this.content = content; + } + + + public Long getId() { + return id; + } + + public String getContent() { + return content; + } + + @Override + public int hashCode() { + return Objects.hash(getId(), getContent()); + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof Car)) { + return false; + } + + final Car other = (Car) object; + return Objects.equals(getId(), other.getId()) + && Objects.equals(getContent(), other.getContent()); + } + +} diff --git a/REST/SpringBoot/web-services-spring-rest-server/logback-access.xml b/REST/SpringBoot/web-services-spring-rest-server/logback-access.xml new file mode 100644 index 0000000..fe219ad --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-server/logback-access.xml @@ -0,0 +1,12 @@ + + + + + %h %l %u [%t] "%r" %s %b "%i{Referer}" "%i{User-Agent}" %D "%header{X-Parent-Id}" "%header{X-Trace-Id}" + utf8 + + true + + + + diff --git a/REST/SpringBoot/web-services-spring-rest-server/logback.xml b/REST/SpringBoot/web-services-spring-rest-server/logback.xml new file mode 100644 index 0000000..a7c8649 --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-server/logback.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + ${CONSOLE_LOG_PATTERN} + utf8 + + true + + + + + + + + + + + + + + + + + + + + diff --git a/REST/SpringBoot/web-services-spring-rest-server/pom.xml b/REST/SpringBoot/web-services-spring-rest-server/pom.xml new file mode 100644 index 0000000..19696fd --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-server/pom.xml @@ -0,0 +1,83 @@ + + 4.0.0 + + web-services-spring-rest-bom + de.spring.webservices + 1.0-SNAPSHOT + + web-services-spring-rest-server + web-services-spring-rest-server + http://gumartinm.name + + + de.spring.webservices + web-services-spring-rest-global + 1.0-SNAPSHOT + + + + org.springframework.boot + spring-boot-starter-web + + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.jvnet.ws.wadl + wadl-core + 1.1.6 + + + org.jvnet.ws.wadl + wadl-client-plugin + 1.1.6 + + + org.springframework + spring-oxm + ${spring.version} + + + + + + io.springfox + springfox-swagger2 + 2.6.1 + + + io.springfox + springfox-swagger-ui + 2.6.1 + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/Application.java b/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/Application.java new file mode 100644 index 0000000..f19933a --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/Application.java @@ -0,0 +1,23 @@ +package de.spring.webservices; + +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class); + } + + + @Bean + CommandLineRunner lookup() { + return args -> { + + }; + } + +} diff --git a/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/configuration/JacksonConfiguration.java b/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/configuration/JacksonConfiguration.java new file mode 100644 index 0000000..2a54a3b --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/configuration/JacksonConfiguration.java @@ -0,0 +1,26 @@ +package de.spring.webservices.configuration; + +import javax.inject.Inject; + +import org.springframework.context.annotation.Configuration; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +@Configuration +public class JacksonConfiguration { + + @Inject + public void configureJackson(ObjectMapper jackson2ObjectMapper) { + jackson2ObjectMapper + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .disable(MapperFeature.DEFAULT_VIEW_INCLUSION) + .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) + .enable(SerializationFeature.INDENT_OUTPUT) + .registerModule(new JavaTimeModule()); + } + +} diff --git a/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/configuration/Swagger2Configuration.java b/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/configuration/Swagger2Configuration.java new file mode 100644 index 0000000..c4ea5bf --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/configuration/Swagger2Configuration.java @@ -0,0 +1,63 @@ +package de.spring.webservices.configuration; + +import static com.google.common.collect.Lists.newArrayList; +import static springfox.documentation.builders.RequestHandlerSelectors.basePackage; +import static springfox.documentation.builders.RequestHandlerSelectors.withMethodAnnotation; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.ResponseMessageBuilder; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger.web.UiConfiguration; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + * Go to URL: http://localhost:8080/swagger-ui.html#/ + * + */ +@Configuration +@EnableSwagger2 +public class Swagger2Configuration { + + @Bean + public Docket documentation() { + return new Docket(DocumentationType.SWAGGER_2) + .select() + .apis(withMethodAnnotation(RequestMapping.class)) + .apis(basePackage("de.spring.webservices.rest.controller")) + .paths(PathSelectors.any()) + .build() + .globalResponseMessage(RequestMethod.GET, + newArrayList(new ResponseMessageBuilder() + .code(500).message("Global server custom error message").build())) + .useDefaultResponseMessages(false) + .apiInfo(metadata()) + .enable(true); + } + + @Bean + UiConfiguration uiConfig() { + return new UiConfiguration(null); + } + + private static ApiInfo metadata() { + return new ApiInfoBuilder() + .title("gumartinm REST API") + .description("Gustavo Martin Morcuende") + .version("1.0-SNAPSHOT") + .contact(doContact()) + .build(); + } + + private static Contact doContact() { + return new Contact("Gustavo Martin", "https://gumartinm.name", ""); + } +} diff --git a/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/configuration/ValidatorConfiguration.java b/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/configuration/ValidatorConfiguration.java new file mode 100644 index 0000000..ff41038 --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/configuration/ValidatorConfiguration.java @@ -0,0 +1,15 @@ +package de.spring.webservices.configuration; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; + +@Configuration +public class ValidatorConfiguration { + + @Bean + public LocalValidatorFactoryBean localValidatorFactoryBean() { + return new LocalValidatorFactoryBean(); + } + +} diff --git a/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/configuration/WadlConfiguration.java b/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/configuration/WadlConfiguration.java new file mode 100644 index 0000000..d128661 --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/configuration/WadlConfiguration.java @@ -0,0 +1,22 @@ +package de.spring.webservices.configuration; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.xml.MarshallingHttpMessageConverter; +import org.springframework.oxm.jaxb.Jaxb2Marshaller; + +@Configuration +public class WadlConfiguration { + + @Bean + public Jaxb2Marshaller marshaller() { + Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); + marshaller.setPackagesToScan("org.jvnet.ws.wadl"); + return marshaller; + } + + @Bean + public MarshallingHttpMessageConverter marshallingHttpMessageConverter(Jaxb2Marshaller marshaller) { + return new MarshallingHttpMessageConverter(marshaller); + } +} diff --git a/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/rest/controller/CarController.java b/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/rest/controller/CarController.java new file mode 100644 index 0000000..a440177 --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/rest/controller/CarController.java @@ -0,0 +1,117 @@ +package de.spring.webservices.rest.controller; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +import de.spring.webservices.domain.Car; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import io.swagger.annotations.ResponseHeader; + +@RestController +@RequestMapping("/api/cars/") +public class CarController { + + private static final Logger LOGGER = LoggerFactory.getLogger(CarController.class); + private static final String TEMPLATE = "Car: %s"; + + @NotNull + private final AtomicLong counter = new AtomicLong(); + + @ApiOperation(value = "Get all available cars", nickname = "getAllCars", responseContainer="List", response = Car.class) + @ApiResponses({ + @ApiResponse(code = 404, message ="Specific getCars not found"), + @ApiResponse(code = 400, message ="Specific getCars invalid input") + }) + @GetMapping(produces = { MediaType.APPLICATION_JSON_UTF8_VALUE }) + @ResponseStatus(HttpStatus.OK) + public List cars() { + final List cars = new ArrayList<>(); + cars.add(new Car(counter.incrementAndGet(), String.format(TEMPLATE, 1))); + cars.add(new Car(counter.incrementAndGet(), String.format(TEMPLATE, 2))); + cars.add(new Car(counter.incrementAndGet(), String.format(TEMPLATE, 3))); + + return cars; + } + + @ApiOperation(value = "Get one car", nickname = "getOneCar", response = Car.class) + @ApiResponses({ + @ApiResponse(code = 404, message ="Specific getCar not found"), + @ApiResponse(code = 400, message ="Specific getCar invalid input") + }) + @GetMapping(value = "{id}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + @ResponseStatus(HttpStatus.OK) + public Car car(@RequestHeader(value = "MY_HEADER", required = false) String specialHeader, + @ApiParam(name = "id", value = "Car id", required = true) @PathVariable("id") long id, + @RequestParam Map params, + @RequestParam(value = "wheel", required = false) String[] wheelParams) { + + if (specialHeader != null) { + LOGGER.info("SPECIAL HEADER: " + specialHeader); + } + + if (params.get("mirror") != null) { + LOGGER.info("MIRROR: " + params.get("mirror")); + } + + if (params.get("window") != null) { + LOGGER.info("WINDOW: " + params.get("window")); + } + + if (wheelParams != null) { + for(String wheel : wheelParams) { + LOGGER.info(wheel); + } + } + + try { + Thread.sleep(10000); //1000 milliseconds is one second. + } catch(InterruptedException ex) { + Thread.currentThread().interrupt(); + } + + + return new Car(counter.incrementAndGet(), String.format(TEMPLATE, id)); + } + + @ApiOperation(code = 201, value = "Create one new car", nickname = "createNewCar") + @ApiResponses({ + @ApiResponse(code = 201, message ="Specific createCar with header", + responseHeaders = { @ResponseHeader(name = HttpHeaders.LOCATION) }, response = Car.class), + @ApiResponse(code = 404, message ="Specific createCar not found"), + @ApiResponse(code = 400, message ="Specific createCar invalid input") + }) + @PostMapping(consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + @ResponseStatus(HttpStatus.CREATED) + public ResponseEntity create(@RequestBody @Valid Car car) { + long count = counter.incrementAndGet(); + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.LOCATION, "/api/cars/" + count); + + return new ResponseEntity<>(new Car(count, String.format(TEMPLATE, count)), headers, HttpStatus.CREATED); + } + +} diff --git a/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/rest/wadl/WADLController.java b/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/rest/wadl/WADLController.java new file mode 100644 index 0000000..1125157 --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-server/src/main/java/de/spring/webservices/rest/wadl/WADLController.java @@ -0,0 +1,236 @@ +package de.spring.webservices.rest.wadl; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; +import javax.xml.namespace.QName; + +import org.apache.commons.lang.StringUtils; +import org.jvnet.ws.wadl.Application; +import org.jvnet.ws.wadl.Doc; +import org.jvnet.ws.wadl.Param; +import org.jvnet.ws.wadl.ParamStyle; +import org.jvnet.ws.wadl.Representation; +import org.jvnet.ws.wadl.Request; +import org.jvnet.ws.wadl.Resource; +import org.jvnet.ws.wadl.Resources; +import org.jvnet.ws.wadl.Response; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.ValueConstants; +import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; + +/** + * Taken from: http://javattitude.com/2014/05/26/wadl-generator-for-spring-rest/ + * + * With some modifications. + * + */ +@Controller +@RequestMapping("/rest.wadl") +public class WADLController { + private static final String NAMESPACE_URI = "http://www.w3.org/2001/XMLSchema" ; + private static final String WADL_TITLE = "Spring REST Service WADL"; + + private final AbstractHandlerMethodMapping handlerMapping; + private final ApplicationContext context; + + @Autowired + public WADLController(AbstractHandlerMethodMapping handlerMapping, ApplicationContext context) { + this.handlerMapping = handlerMapping; + this.context = context; + } + + @RequestMapping(produces = { MediaType.APPLICATION_XML_VALUE }, method=RequestMethod.GET ) + public @ResponseBody Application generateWadl(HttpServletRequest request) { + Application result = new Application(); + + Doc doc = new Doc(); + doc.setTitle(WADL_TITLE); + result.getDoc().add(doc); + + Resources wadlResources = new Resources(); + wadlResources.setBase(getBaseUrl(request)); + + handlerMapping.getHandlerMethods().forEach( (mappingInfo, handlerMethod) -> { + Object object = handlerMethod.getBean(); + Object bean = context.getBean(object.toString()); + if(!bean.getClass().isAnnotationPresent(RestController.class)) { + return; + } + + mappingInfo.getMethodsCondition().getMethods().forEach(httpMethod -> { + Resource wadlResource = null; + org.jvnet.ws.wadl.Method wadlMethod = new org.jvnet.ws.wadl.Method(); + + Set pattern = mappingInfo.getPatternsCondition().getPatterns(); + for (String uri : pattern) { + wadlResource = createOrFind(uri, wadlResources); + wadlResource.setPath(uri); + } + + wadlMethod.setName(httpMethod.name()); + Method javaMethod = handlerMethod.getMethod(); + wadlMethod.setId(javaMethod.getName()); + Doc wadlDocMethod = new Doc(); + wadlDocMethod.setTitle(javaMethod.getDeclaringClass().getSimpleName() + "." + javaMethod.getName()); + wadlMethod.getDoc().add(wadlDocMethod); + + // Request + Request wadlRequest = new Request(); + Annotation[][] annotations = javaMethod.getParameterAnnotations(); + Class[] paramTypes = javaMethod.getParameterTypes(); + int i = 0; + for (Annotation[] annotation : annotations) { + Class paramType =paramTypes[i]; + i++; + for (Annotation annotation2 : annotation) { + + Param wadlParam = doParam(annotation2, paramType); + if (wadlParam != null) { + wadlRequest.getParam().add(wadlParam); + } + } + } + if (!wadlRequest.getParam().isEmpty() ) { + wadlMethod.setRequest(wadlRequest); + } + + // Response + Set mediaTypes = mappingInfo.getProducesCondition().getProducibleMediaTypes(); + if (!mediaTypes.isEmpty()) { + ResponseStatus status = handlerMethod.getMethodAnnotation(ResponseStatus.class); + Response wadlResponse = doResponse(mediaTypes, status); + wadlMethod.getResponse().add(wadlResponse); + } + + + wadlResource.getMethodOrResource().add(wadlMethod); + }); + + }); + result.getResources().add(wadlResources); + + return result; + } + + private Param doParam(Annotation annotation2, Class paramType) { + Param wadlParam = null; + + if (annotation2 instanceof RequestParam ) { + RequestParam param = (RequestParam)annotation2; + + wadlParam = new Param(); + QName nm = convertJavaToXMLType(paramType); + if (StringUtils.isNotEmpty(nm.getLocalPart())) { + wadlParam.setType(nm); + } + wadlParam.setName(param.value()); + + + if (!ValueConstants.DEFAULT_NONE.equals(param.defaultValue())) { + String defaultValue = cleanDefault(param.defaultValue()); + if (StringUtils.isNotEmpty(defaultValue) ) { + wadlParam.setDefault(defaultValue); + } + } + + wadlParam.setStyle(ParamStyle.QUERY); + wadlParam.setRequired(param.required()); + } else if (annotation2 instanceof PathVariable ) { + PathVariable param = (PathVariable)annotation2; + + wadlParam = new Param(); + QName nm = convertJavaToXMLType(paramType); + if (StringUtils.isNotEmpty(nm.getLocalPart())) { + wadlParam.setType(nm); + } + wadlParam.setName(param.value()); + + + wadlParam.setStyle(ParamStyle.TEMPLATE); + wadlParam.setRequired(true); + } + + return wadlParam; + } + + private Response doResponse(Set mediaTypes, ResponseStatus status) { + Response wadlResponse = new Response(); + + mediaTypes.forEach(mediaType -> { + Representation wadlRepresentation = new Representation(); + wadlRepresentation.setMediaType(mediaType.toString()); + wadlResponse.getRepresentation().add(wadlRepresentation); + }); + + wadlResponse.getStatus().add(getResposeValue(status)); + + return wadlResponse; + } + + private long getResposeValue(ResponseStatus status) { + if(status == null) { + return HttpStatus.OK.value(); + } else { + HttpStatus httpcode = status.value(); + return httpcode.value(); + } + } + + private QName convertJavaToXMLType(Class type) { + QName nm = new QName(""); + String classname = type.toString(); + classname = classname.toLowerCase(); + + if (classname.indexOf("string") >= 0) { + nm = new QName(NAMESPACE_URI, "string", "xs"); + } else if (classname.indexOf("integer") >= 0) { + nm = new QName(NAMESPACE_URI, "int", "xs"); + } else if (classname.indexOf("long") >= 0) { + nm = new QName(NAMESPACE_URI, "long", "xs"); + } + + return nm; + } + + private Resource createOrFind(String uri, Resources wadResources) { + List current = wadResources.getResource(); + for(Resource resource:current) { + if(resource.getPath().equalsIgnoreCase(uri)){ + return resource; + } + } + Resource wadlResource = new Resource(); + current.add(wadlResource); + return wadlResource; + } + + private String getBaseUrl(HttpServletRequest request) { + String requestUri = request.getRequestURI(); + int index = requestUri.lastIndexOf('/'); + requestUri = requestUri.substring(0, index); + + return request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + requestUri; + } + + private String cleanDefault(String value) { + value = value.replaceAll("\t", ""); + value = value.replaceAll("\n", ""); + return value; + } +} diff --git a/REST/SpringBoot/web-services-spring-rest-server/src/main/resources/application.yml b/REST/SpringBoot/web-services-spring-rest-server/src/main/resources/application.yml new file mode 100644 index 0000000..7a01bea --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-server/src/main/resources/application.yml @@ -0,0 +1,13 @@ +spring: + application: + name: web-services-spring-rest-server + main: + banner-mode: "LOG" + +server: + port: 8080 + +springfox.documentation.swagger.v2.path: /schema + +logging: + config: classpath:log4j2.xml diff --git a/REST/SpringBoot/web-services-spring-rest-server/src/main/resources/banner.txt b/REST/SpringBoot/web-services-spring-rest-server/src/main/resources/banner.txt new file mode 100644 index 0000000..2782065 --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-server/src/main/resources/banner.txt @@ -0,0 +1,9 @@ + _____ _ __ __ _ _____ _ + / ____| (_) \ \ / / | | / ____| (_) + | (___ _ __ _ __ _ _ __ __ _ \ \ /\ / /__| |__ | (___ ___ _ ____ ___ ___ ___ ___ + \___ \| '_ \| '__| | '_ \ / _` | \ \/ \/ / _ \ '_ \ \___ \ / _ \ '__\ \ / / |/ __/ _ \/ __| + ____) | |_) | | | | | | | (_| | \ /\ / __/ |_) | ____) | __/ | \ V /| | (_| __/\__ \ + |_____/| .__/|_| |_|_| |_|\__, | \/ \/ \___|_.__/ |_____/ \___|_| \_/ |_|\___\___||___/ + | | __/ | + |_| |___/ + ${application.title} ${application.formatted-version} \ No newline at end of file diff --git a/REST/SpringBoot/web-services-spring-rest-server/src/main/resources/log4j2.xml b/REST/SpringBoot/web-services-spring-rest-server/src/main/resources/log4j2.xml new file mode 100644 index 0000000..21f1ac3 --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-server/src/main/resources/log4j2.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/REST/SpringBoot/web-services-spring-rest-server/src/main/webapp/WEB-INF/web.xml b/REST/SpringBoot/web-services-spring-rest-server/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..6c9c1d0 --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-server/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,41 @@ + + + + Spring REST Services: example + + + + org.springframework.web.context.ContextLoaderListener + + + + + spring.profiles.active + ${environment.profile} + contextConfigLocation + + classpath*:spring-configuration/*.xml + + + + + + spring-rest + org.springframework.web.servlet.DispatcherServlet + 1 + true + + contextConfigLocation + classpath*:spring-configuration/mvc/rest/*.xml + + + + + spring-rest + + /* + + + diff --git a/REST/SpringBoot/web-services-spring-rest-server/src/test/java/de/spring/webservices/rest/controller/CarControllerIntegrationTest.java b/REST/SpringBoot/web-services-spring-rest-server/src/test/java/de/spring/webservices/rest/controller/CarControllerIntegrationTest.java new file mode 100644 index 0000000..db04517 --- /dev/null +++ b/REST/SpringBoot/web-services-spring-rest-server/src/test/java/de/spring/webservices/rest/controller/CarControllerIntegrationTest.java @@ -0,0 +1,105 @@ +package de.spring.webservices.rest.controller; + +import static org.hamcrest.Matchers.any; +import static org.hamcrest.Matchers.is; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import javax.inject.Inject; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +//import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import de.spring.webservices.domain.Car; + + +@RunWith(SpringRunner.class) +@WebMvcTest(CarController.class) +public class CarControllerIntegrationTest { + + // For injecting and mocking services which could be used in the Controller under test. + //@MockBean + //private CarService carService; + + @Inject + private WebApplicationContext context; + + @Inject + private ObjectMapper objectMapper; + + private MockMvc mockMvc; + + @Before + public void setup() { + mockMvc = MockMvcBuilders + .webAppContextSetup(context) + .build(); + + objectMapper + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .disable(MapperFeature.DEFAULT_VIEW_INCLUSION) + .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) + .enable(SerializationFeature.INDENT_OUTPUT) + .registerModule(new JavaTimeModule()); + } + + @Test + public void testWhenGetAllCarsThenRetrieveJsonValues() throws Exception { + mockMvc.perform(get("/api/cars/") + .accept(MediaType.APPLICATION_JSON_UTF8)) + + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].id", any(Integer.class))) + .andExpect(jsonPath("$[0].content", is("Car: 1"))) + .andExpect(jsonPath("$[1].content", is("Car: 2"))) + .andExpect(jsonPath("$[1].id", any(Integer.class))) + .andExpect(jsonPath("$[2].content", is("Car: 3"))) + .andExpect(jsonPath("$[2].id", any(Integer.class))) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)); + } + + @Test + public void testWhenGetOneCarThenRetrieveJsonValue() throws Exception { + mockMvc.perform(get("/api/cars/{id}", 1L) + .accept(MediaType.APPLICATION_JSON_UTF8)) + + .andExpect(status().isOk()) + .andExpect(jsonPath("id", any(Integer.class))) + .andExpect(jsonPath("content", is("Car: 1"))) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)); + } + + @Test + public void testWhenCreateNewCarThenRetrieveJsonValue() throws Exception { + Car car = new Car(2L, "nothing"); + mockMvc.perform(post("/api/cars/") + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content(objectMapper.writeValueAsString(car)) + .accept(MediaType.APPLICATION_JSON_UTF8)) + + .andExpect(status().isCreated()) + .andExpect(jsonPath("id", any(Integer.class))) + .andExpect(jsonPath("content", is("Car: 2"))) + .andExpect(header().string(HttpHeaders.LOCATION, "/api/cars/2")) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)); + } +} diff --git a/REST/SpringBoot/web-services-spring-rest-server/src/test/resources/application.yml b/REST/SpringBoot/web-services-spring-rest-server/src/test/resources/application.yml new file mode 100644 index 0000000..e69de29 diff --git a/REST/web-services-spring-rest-client/pom.xml b/REST/web-services-spring-rest-client/pom.xml deleted file mode 100644 index 276b3db..0000000 --- a/REST/web-services-spring-rest-client/pom.xml +++ /dev/null @@ -1,61 +0,0 @@ - - 4.0.0 - - web-services-spring-rest - de.spring.webservices - 1.0-SNAPSHOT - - web-services-spring-rest-client - web-services-spring-rest-client - http://gumartinm.name - - - de.spring.webservices - web-services-spring-rest-global - 1.0-SNAPSHOT - - - - org.springframework - spring-core - - - org.springframework - spring-webmvc - - - - - com.fasterxml.jackson.core - jackson-databind - - - - - junit - junit - test - - - org.springframework - spring-test - test - - - org.mockito - mockito-core - test - - - - - - ${basedir}/src/main/resources/ - - **/*.* - - - - - \ No newline at end of file diff --git a/REST/web-services-spring-rest-client/src/main/java/de/spring/webservices/main/MainTest.java b/REST/web-services-spring-rest-client/src/main/java/de/spring/webservices/main/MainTest.java deleted file mode 100644 index 0f066ad..0000000 --- a/REST/web-services-spring-rest-client/src/main/java/de/spring/webservices/main/MainTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package de.spring.webservices.main; - -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; - -import de.spring.webservices.rest.business.service.BusinessService; - - -/** - * This class is used just like a nice example about how to write and run client - * code which will send data to and from the Web Services. - * - */ -public class MainTest { - public ApplicationContext context; - - /** - * @param args - */ - public static void main(final String[] args) { - final MainTest test = new MainTest(); - - test.context = new ClassPathXmlApplicationContext( - "classpath:spring-configuration/rest-config.xml"); - - final BusinessService example = - (BusinessService) test.context.getBean("businessService"); - - example.doSomethingWithCars(); - - example.doSomethingWithCar(66L); - - example.createsNewCar(); - } -} diff --git a/REST/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/business/service/BusinessService.java b/REST/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/business/service/BusinessService.java deleted file mode 100644 index ca7a7f2..0000000 --- a/REST/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/business/service/BusinessService.java +++ /dev/null @@ -1,11 +0,0 @@ -package de.spring.webservices.rest.business.service; - - -public interface BusinessService { - - public void doSomethingWithCars(); - - public void doSomethingWithCar(long id); - - public void createsNewCar(); -} diff --git a/REST/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/business/service/impl/BusinessServiceImpl.java b/REST/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/business/service/impl/BusinessServiceImpl.java deleted file mode 100644 index b6367e8..0000000 --- a/REST/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/business/service/impl/BusinessServiceImpl.java +++ /dev/null @@ -1,53 +0,0 @@ -package de.spring.webservices.rest.business.service.impl; - -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import de.spring.webservices.domain.Car; -import de.spring.webservices.rest.business.service.BusinessService; -import de.spring.webservices.rest.client.service.CarClientService; - -@Service("businessService") -public class BusinessServiceImpl implements BusinessService { - private static final Logger LOGGER = LoggerFactory.getLogger(BusinessServiceImpl.class); - - private final CarClientService carClientService; - - @Autowired - public BusinessServiceImpl(CarClientService carClientService) { - this.carClientService = carClientService; - } - - - @Override - public void doSomethingWithCars() { - List cars = carClientService.doGetCars(); - LOGGER.info("Retrieved cars"); - for (Car car : cars) { - LOGGER.info("car: " + car.getId()); - LOGGER.info(car.getContent()); - } - } - - @Override - public void doSomethingWithCar(long id) { - Car car = carClientService.doGetCar(id); - LOGGER.info("Retrieved car"); - LOGGER.info("car: " + car.getId()); - LOGGER.info(car.getContent()); - } - - @Override - public void createsNewCar() { - Car newCar = new Car(666L, "just_a_test"); - - Car car = carClientService.doNewCar(newCar); - LOGGER.info("New car"); - LOGGER.info("car: " + car.getId()); - LOGGER.info(car.getContent()); - } -} diff --git a/REST/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/client/service/CarClientService.java b/REST/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/client/service/CarClientService.java deleted file mode 100644 index 23966ba..0000000 --- a/REST/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/client/service/CarClientService.java +++ /dev/null @@ -1,14 +0,0 @@ -package de.spring.webservices.rest.client.service; - -import java.util.List; - -import de.spring.webservices.domain.Car; - -public interface CarClientService { - - public List doGetCars(); - - public Car doGetCar(long id); - - public Car doNewCar(Car car); -} diff --git a/REST/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/client/service/impl/CarClientServiceImpl.java b/REST/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/client/service/impl/CarClientServiceImpl.java deleted file mode 100644 index bdb540b..0000000 --- a/REST/web-services-spring-rest-client/src/main/java/de/spring/webservices/rest/client/service/impl/CarClientServiceImpl.java +++ /dev/null @@ -1,58 +0,0 @@ -package de.spring.webservices.rest.client.service.impl; - -import java.net.URI; -import java.util.Arrays; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.web.client.RestTemplate; - -import de.spring.webservices.domain.Car; -import de.spring.webservices.rest.client.service.CarClientService; - -@Service("carClientService") -public class CarClientServiceImpl implements CarClientService { - private static final Logger LOGGER = LoggerFactory.getLogger(CarClientServiceImpl.class); - - private final String apiCarsUrl; - private final String apiCarUrl; - private final RestTemplate restTemplate; - - @Autowired - public CarClientServiceImpl(@Value("${url.base}${url.cars}") String apiCarsUrl, - @Value("${url.base}${url.car}") String apiCarUrl, RestTemplate restTemplate) { - this.apiCarsUrl = apiCarsUrl; - this.apiCarUrl = apiCarUrl; - this.restTemplate = restTemplate; - } - - - @Override - public List doGetCars() { - ResponseEntity responseEntity = restTemplate.getForEntity(apiCarsUrl, Car[].class); - - return Arrays.asList(responseEntity.getBody()); - } - - @Override - public Car doGetCar(long id) { - ResponseEntity responseEntity = restTemplate.getForEntity( - apiCarUrl.replace(":id", String.valueOf(id)), Car.class); - - return responseEntity.getBody(); - } - - @Override - public Car doNewCar(Car car) { - ResponseEntity responseEntity = restTemplate.postForEntity(apiCarsUrl, car, Car.class); - URI newCarLocation = responseEntity.getHeaders().getLocation(); - LOGGER.info("new car location: " + newCarLocation.getPath()); - - return responseEntity.getBody(); - } -} diff --git a/REST/web-services-spring-rest-client/src/main/resources/log4j2.xml b/REST/web-services-spring-rest-client/src/main/resources/log4j2.xml deleted file mode 100644 index ee36b97..0000000 --- a/REST/web-services-spring-rest-client/src/main/resources/log4j2.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/REST/web-services-spring-rest-client/src/main/resources/rest.properties b/REST/web-services-spring-rest-client/src/main/resources/rest.properties deleted file mode 100644 index 00255b0..0000000 --- a/REST/web-services-spring-rest-client/src/main/resources/rest.properties +++ /dev/null @@ -1,4 +0,0 @@ -url.base = http://localhost:8080/web-services-spring-rest-server/spring-rest/ - -url.cars = api/cars/ -url.car = api/cars/:id \ No newline at end of file diff --git a/REST/web-services-spring-rest-client/src/main/resources/spring-configuration/rest-config.xml b/REST/web-services-spring-rest-client/src/main/resources/spring-configuration/rest-config.xml deleted file mode 100644 index 5f4e873..0000000 --- a/REST/web-services-spring-rest-client/src/main/resources/spring-configuration/rest-config.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/REST/web-services-spring-rest-client/src/test/java/de/spring/webservices/rest/business/service/BusinessServiceTest.java b/REST/web-services-spring-rest-client/src/test/java/de/spring/webservices/rest/business/service/BusinessServiceTest.java deleted file mode 100644 index 53665c4..0000000 --- a/REST/web-services-spring-rest-client/src/test/java/de/spring/webservices/rest/business/service/BusinessServiceTest.java +++ /dev/null @@ -1,67 +0,0 @@ -package de.spring.webservices.rest.business.service; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; - -import de.spring.webservices.domain.Car; -import de.spring.webservices.rest.business.service.impl.BusinessServiceImpl; -import de.spring.webservices.rest.client.service.CarClientService; - -public class BusinessServiceTest { - - private CarClientService carClientService; - private BusinessService businessService; - - @Before - public void createTest() { - carClientService = mock(CarClientService.class); - businessService = new BusinessServiceImpl(carClientService); - } - - @Test - public void whenDoSomethingWithCarsThenInvokeDoGetCars() { - Car expectedOne = new Car(66L, "test"); - Car expectedTwo = new Car(99L, "example"); - List expected = new ArrayList<>(); - expected.add(expectedOne); - expected.add(expectedTwo); - when(carClientService.doGetCars()).thenReturn(expected); - - businessService.doSomethingWithCars(); - - verify(carClientService, times(1)).doGetCars(); - } - - @Test - public void whenDoSomethingWithOneCarhenInvokeDoGetCar() { - Long id = 66L; - Car expected = new Car(66L, "test"); - - when(carClientService.doGetCar(id)).thenReturn(expected); - - businessService.doSomethingWithCar(id); - - verify(carClientService, times(1)).doGetCar(id); - } - - @Test - public void whenCreateNewCarThenCreateNewOne() { - Car expected = new Car(66L, "test"); - ArgumentCaptor argCar = ArgumentCaptor.forClass(Car.class); - - when(carClientService.doNewCar(argCar.capture())).thenReturn(expected); - - businessService.createsNewCar(); - - verify(carClientService, times(1)).doNewCar(argCar.getValue()); - } -} diff --git a/REST/web-services-spring-rest-client/src/test/java/de/spring/webservices/rest/client/service/CarClientServiceIntegrationTest.java b/REST/web-services-spring-rest-client/src/test/java/de/spring/webservices/rest/client/service/CarClientServiceIntegrationTest.java deleted file mode 100644 index b5696be..0000000 --- a/REST/web-services-spring-rest-client/src/test/java/de/spring/webservices/rest/client/service/CarClientServiceIntegrationTest.java +++ /dev/null @@ -1,117 +0,0 @@ -package de.spring.webservices.rest.client.service; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.content; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; -import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Before; -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.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.web.client.MockRestServiceServer; -import org.springframework.web.client.RestTemplate; - -import com.fasterxml.jackson.core.JsonProcessingException; - -import de.spring.webservices.domain.Car; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration("classpath*:spring-configuration/rest-config.xml") -public class CarClientServiceIntegrationTest { - - @Value("${url.base}${url.cars}") - private String apiCarsUrl; - - @Value("${url.base}${url.car}") - private String apiCarUrl; - - @Autowired - private RestTemplate restTemplate; - - @Autowired - private CarClientService carClientService; - - @Autowired - private Jackson2ObjectMapperFactoryBean jsonObjectMapperFactory; - - private MockRestServiceServer mockServer; - - @Before - public void createTest() { - mockServer = MockRestServiceServer.createServer(restTemplate); - } - - @Test - public void whenGetAllCarsThenRetrieveRequestedCars() throws JsonProcessingException { - Car expectedOne = new Car(66L, "test"); - List expected = new ArrayList<>(); - expected.add(expectedOne); - - mockServer.expect(requestTo(apiCarsUrl)) - .andExpect(method(HttpMethod.GET)) - .andRespond(withSuccess(asJsonString(expected), MediaType.APPLICATION_JSON_UTF8)); - - List cars = carClientService.doGetCars(); - - mockServer.verify(); - - assertEquals(1, cars.size()); - assertEquals(expectedOne, cars.get(0)); - } - - @Test - public void whenGetCarByIdThenRetrieveRequestedCar() throws JsonProcessingException { - Long id = 66L; - Car expected = new Car(66L, "test"); - - mockServer.expect(requestTo(apiCarUrl.replace(":id", String.valueOf(id)))) - .andExpect(method(HttpMethod.GET)) - .andRespond(withSuccess(asJsonString(expected), MediaType.APPLICATION_JSON_UTF8)); - - Car car = carClientService.doGetCar(id); - - mockServer.verify(); - - assertNotNull(car); - assertEquals(expected, car); - } - - @Test - public void whenCreateNewCarThenRetrieveCreatedCar() throws JsonProcessingException { - Long expectedId = 66L; - HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.LOCATION, "/api/cars/" + String.valueOf(expectedId)); - Car expected = new Car(expectedId, "test"); - - mockServer.expect(requestTo(apiCarsUrl)) - .andExpect(method(HttpMethod.POST)) - .andExpect(content() - .string(asJsonString(expected))) - .andRespond(withSuccess(asJsonString(expected), MediaType.APPLICATION_JSON_UTF8) - .headers(headers)); - - Car car = carClientService.doNewCar(expected); - - mockServer.verify(); - - assertNotNull(car); - assertEquals(expected, car); - } - - private String asJsonString(final Object obj) throws JsonProcessingException { - return jsonObjectMapperFactory.getObject().writeValueAsString(obj); - } -} diff --git a/REST/web-services-spring-rest-global/pom.xml b/REST/web-services-spring-rest-global/pom.xml deleted file mode 100644 index c726115..0000000 --- a/REST/web-services-spring-rest-global/pom.xml +++ /dev/null @@ -1,12 +0,0 @@ - - 4.0.0 - - web-services-spring-rest - de.spring.webservices - 1.0-SNAPSHOT - - web-services-spring-rest-global - web-services-spring-rest-global - http://gumartinm.name - diff --git a/REST/web-services-spring-rest-global/src/main/java/de/spring/webservices/domain/Car.java b/REST/web-services-spring-rest-global/src/main/java/de/spring/webservices/domain/Car.java deleted file mode 100644 index cdfaa07..0000000 --- a/REST/web-services-spring-rest-global/src/main/java/de/spring/webservices/domain/Car.java +++ /dev/null @@ -1,58 +0,0 @@ -package de.spring.webservices.domain; - -public class Car { - - private final Long id; - private final String content; - - // Required by Jackson :/ - public Car() { - this.id = null; - this.content = null; - } - - public Car(Long id, String content) { - this.id = id; - this.content = content; - } - - - public Long getId() { - return id; - } - - public String getContent() { - return content; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((content == null) ? 0 : content.hashCode()); - result = prime * result + ((id == null) ? 0 : id.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Car other = (Car) obj; - if (content == null) { - if (other.content != null) - return false; - } else if (!content.equals(other.content)) - return false; - if (id == null) { - if (other.id != null) - return false; - } else if (!id.equals(other.id)) - return false; - return true; - } -} diff --git a/REST/web-services-spring-rest-server/pom.xml b/REST/web-services-spring-rest-server/pom.xml deleted file mode 100644 index 144a67c..0000000 --- a/REST/web-services-spring-rest-server/pom.xml +++ /dev/null @@ -1,139 +0,0 @@ - - 4.0.0 - - web-services-spring-rest - de.spring.webservices - 1.0-SNAPSHOT - - web-services-spring-rest-server - war - web-services-spring-rest-server - http://gumartinm.name - - - de.spring.webservices - web-services-spring-rest-global - 1.0-SNAPSHOT - - - - org.springframework - spring-core - - - org.springframework - spring-webmvc - - - - - javax.servlet - javax.servlet-api - provided - - - - - com.fasterxml.jackson.core - jackson-databind - - - - - org.jvnet.ws.wadl - wadl-core - 1.1.6 - - - org.jvnet.ws.wadl - wadl-client-plugin - 1.1.6 - - - org.springframework - spring-oxm - 4.2.4.RELEASE - - - - - io.springfox - springfox-swagger1 - 2.3.1 - - - io.springfox - springfox-swagger-ui - 2.3.1 - - - - - javax.validation - validation-api - - - org.hibernate - hibernate-validator - - - - - junit - junit - test - - - org.springframework - spring-test - test - - - org.mockito - mockito-core - test - - - - - org.hamcrest - hamcrest-core - test - - - org.hamcrest - hamcrest-library - test - - - com.jayway.jsonpath - json-path - test - - - - ${project.artifactId} - - - ${basedir}/src/main/webapp - - **/*.* - - - - ${basedir}/src/main/resources/ - - **/*.* - - - - - - - org.apache.maven.plugins - maven-war-plugin - - - - diff --git a/REST/web-services-spring-rest-server/src/main/java/de/spring/webservices/doc/Swagger2Configuration.java b/REST/web-services-spring-rest-server/src/main/java/de/spring/webservices/doc/Swagger2Configuration.java deleted file mode 100644 index df71590..0000000 --- a/REST/web-services-spring-rest-server/src/main/java/de/spring/webservices/doc/Swagger2Configuration.java +++ /dev/null @@ -1,58 +0,0 @@ -package de.spring.webservices.doc; - -import static com.google.common.collect.Lists.newArrayList; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; - -import springfox.documentation.builders.ApiInfoBuilder; -import springfox.documentation.builders.PathSelectors; -import springfox.documentation.builders.RequestHandlerSelectors; -import springfox.documentation.builders.ResponseMessageBuilder; -import springfox.documentation.service.ApiInfo; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spring.web.plugins.Docket; -import springfox.documentation.swagger.web.UiConfiguration; -import springfox.documentation.swagger1.annotations.EnableSwagger; - -@Configuration -@EnableWebMvc -@EnableSwagger -@ComponentScan("de.spring.webservices.rest.controller") -public class Swagger2Configuration { - - @Bean - public Docket documentation() { - return new Docket(DocumentationType.SWAGGER_12) - .select() - .apis(RequestHandlerSelectors.withMethodAnnotation(RequestMapping.class)) - .paths(PathSelectors.any()) - .build() - .globalResponseMessage(RequestMethod.GET, - newArrayList(new ResponseMessageBuilder() - .code(500).message("Global server custom error message").build())) - .pathMapping("/") - .useDefaultResponseMessages(false) - .apiInfo(metadata()) - .enable(true); - } - - @Bean - UiConfiguration uiConfig() { - return UiConfiguration.DEFAULT; - } - - private static ApiInfo metadata() { - return new ApiInfoBuilder() - .title("gumartinm REST API") - .description("Gustavo Martin Morcuende") - .version("1.0-SNAPSHOT") - .contact("gumartinm.name") - .build(); - } - -} diff --git a/REST/web-services-spring-rest-server/src/main/java/de/spring/webservices/rest/controller/CarController.java b/REST/web-services-spring-rest-server/src/main/java/de/spring/webservices/rest/controller/CarController.java deleted file mode 100644 index 49acf64..0000000 --- a/REST/web-services-spring-rest-server/src/main/java/de/spring/webservices/rest/controller/CarController.java +++ /dev/null @@ -1,112 +0,0 @@ -package de.spring.webservices.rest.controller; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; - -import de.spring.webservices.domain.Car; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import io.swagger.annotations.ResponseHeader; - -@RestController -@RequestMapping("/api/cars/") -public class CarController { - private static final Logger LOGGER = LoggerFactory.getLogger(CarController.class); - private static final String TEMPLATE = "Car: %s"; - - private final AtomicLong counter = new AtomicLong(); - - @ApiOperation(value = "Get all available cars", nickname = "getAllCars", responseContainer="List", response = Car.class) - @ApiResponses({ - @ApiResponse(code = 404, message ="Specific getCars not found"), - @ApiResponse(code = 400, message ="Specific getCars invalid input") - }) - @RequestMapping(produces = { MediaType.APPLICATION_JSON_UTF8_VALUE }, method = RequestMethod.GET) - @ResponseStatus(HttpStatus.OK) - public List cars() { - final List cars = new ArrayList<>(); - cars.add(new Car(counter.incrementAndGet(), String.format(TEMPLATE, 1))); - cars.add(new Car(counter.incrementAndGet(), String.format(TEMPLATE, 2))); - cars.add(new Car(counter.incrementAndGet(), String.format(TEMPLATE, 3))); - - return cars; - } - - @ApiOperation(value = "Get one car", nickname = "getOneCar", response = Car.class) - @ApiResponses({ - @ApiResponse(code = 404, message ="Specific getCar not found"), - @ApiResponse(code = 400, message ="Specific getCar invalid input") - }) - @RequestMapping(value = "{id}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.GET) - @ResponseStatus(HttpStatus.OK) - public Car car(@RequestHeader(value = "MY_HEADER", required = false) String specialHeader, - @ApiParam(name = "id", value = "Car id", required = true) @PathVariable("id") long id, - @RequestParam Map params, - @RequestParam(value = "wheel", required = false) String[] wheelParams) { - - if (specialHeader != null) { - LOGGER.info("SPECIAL HEADER: " + specialHeader); - } - - if (params.get("mirror") != null) { - LOGGER.info("MIRROR: " + params.get("mirror")); - } - - if (params.get("window") != null) { - LOGGER.info("WINDOW: " + params.get("window")); - } - - if (wheelParams != null) { - for(String wheel : wheelParams) { - LOGGER.info(wheel); - } - } - - try { - Thread.sleep(10000); //1000 milliseconds is one second. - } catch(InterruptedException ex) { - Thread.currentThread().interrupt(); - } - - - return new Car(counter.incrementAndGet(), String.format(TEMPLATE, id)); - } - - @ApiOperation(code = 201, value = "Create one new car", nickname = "createNewCar") - @ApiResponses({ - @ApiResponse(code = 201, message ="Specific createCar with header", - responseHeaders = { @ResponseHeader(name = HttpHeaders.LOCATION) }, response = Car.class), - @ApiResponse(code = 404, message ="Specific createCar not found"), - @ApiResponse(code = 400, message ="Specific createCar invalid input") - }) - @RequestMapping(consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, - produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.POST) - @ResponseStatus(HttpStatus.CREATED) - public ResponseEntity create(@RequestBody Car car) { - long count = counter.incrementAndGet(); - HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.LOCATION, "/api/cars/" + count); - - return new ResponseEntity<>(new Car(count, String.format(TEMPLATE, count)), headers, HttpStatus.CREATED); - } - -} diff --git a/REST/web-services-spring-rest-server/src/main/java/de/spring/webservices/rest/wadl/WADLController.java b/REST/web-services-spring-rest-server/src/main/java/de/spring/webservices/rest/wadl/WADLController.java deleted file mode 100644 index 1125157..0000000 --- a/REST/web-services-spring-rest-server/src/main/java/de/spring/webservices/rest/wadl/WADLController.java +++ /dev/null @@ -1,236 +0,0 @@ -package de.spring.webservices.rest.wadl; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.List; -import java.util.Set; - -import javax.servlet.http.HttpServletRequest; -import javax.xml.namespace.QName; - -import org.apache.commons.lang.StringUtils; -import org.jvnet.ws.wadl.Application; -import org.jvnet.ws.wadl.Doc; -import org.jvnet.ws.wadl.Param; -import org.jvnet.ws.wadl.ParamStyle; -import org.jvnet.ws.wadl.Representation; -import org.jvnet.ws.wadl.Request; -import org.jvnet.ws.wadl.Resource; -import org.jvnet.ws.wadl.Resources; -import org.jvnet.ws.wadl.Response; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.bind.annotation.ValueConstants; -import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping; -import org.springframework.web.servlet.mvc.method.RequestMappingInfo; - -/** - * Taken from: http://javattitude.com/2014/05/26/wadl-generator-for-spring-rest/ - * - * With some modifications. - * - */ -@Controller -@RequestMapping("/rest.wadl") -public class WADLController { - private static final String NAMESPACE_URI = "http://www.w3.org/2001/XMLSchema" ; - private static final String WADL_TITLE = "Spring REST Service WADL"; - - private final AbstractHandlerMethodMapping handlerMapping; - private final ApplicationContext context; - - @Autowired - public WADLController(AbstractHandlerMethodMapping handlerMapping, ApplicationContext context) { - this.handlerMapping = handlerMapping; - this.context = context; - } - - @RequestMapping(produces = { MediaType.APPLICATION_XML_VALUE }, method=RequestMethod.GET ) - public @ResponseBody Application generateWadl(HttpServletRequest request) { - Application result = new Application(); - - Doc doc = new Doc(); - doc.setTitle(WADL_TITLE); - result.getDoc().add(doc); - - Resources wadlResources = new Resources(); - wadlResources.setBase(getBaseUrl(request)); - - handlerMapping.getHandlerMethods().forEach( (mappingInfo, handlerMethod) -> { - Object object = handlerMethod.getBean(); - Object bean = context.getBean(object.toString()); - if(!bean.getClass().isAnnotationPresent(RestController.class)) { - return; - } - - mappingInfo.getMethodsCondition().getMethods().forEach(httpMethod -> { - Resource wadlResource = null; - org.jvnet.ws.wadl.Method wadlMethod = new org.jvnet.ws.wadl.Method(); - - Set pattern = mappingInfo.getPatternsCondition().getPatterns(); - for (String uri : pattern) { - wadlResource = createOrFind(uri, wadlResources); - wadlResource.setPath(uri); - } - - wadlMethod.setName(httpMethod.name()); - Method javaMethod = handlerMethod.getMethod(); - wadlMethod.setId(javaMethod.getName()); - Doc wadlDocMethod = new Doc(); - wadlDocMethod.setTitle(javaMethod.getDeclaringClass().getSimpleName() + "." + javaMethod.getName()); - wadlMethod.getDoc().add(wadlDocMethod); - - // Request - Request wadlRequest = new Request(); - Annotation[][] annotations = javaMethod.getParameterAnnotations(); - Class[] paramTypes = javaMethod.getParameterTypes(); - int i = 0; - for (Annotation[] annotation : annotations) { - Class paramType =paramTypes[i]; - i++; - for (Annotation annotation2 : annotation) { - - Param wadlParam = doParam(annotation2, paramType); - if (wadlParam != null) { - wadlRequest.getParam().add(wadlParam); - } - } - } - if (!wadlRequest.getParam().isEmpty() ) { - wadlMethod.setRequest(wadlRequest); - } - - // Response - Set mediaTypes = mappingInfo.getProducesCondition().getProducibleMediaTypes(); - if (!mediaTypes.isEmpty()) { - ResponseStatus status = handlerMethod.getMethodAnnotation(ResponseStatus.class); - Response wadlResponse = doResponse(mediaTypes, status); - wadlMethod.getResponse().add(wadlResponse); - } - - - wadlResource.getMethodOrResource().add(wadlMethod); - }); - - }); - result.getResources().add(wadlResources); - - return result; - } - - private Param doParam(Annotation annotation2, Class paramType) { - Param wadlParam = null; - - if (annotation2 instanceof RequestParam ) { - RequestParam param = (RequestParam)annotation2; - - wadlParam = new Param(); - QName nm = convertJavaToXMLType(paramType); - if (StringUtils.isNotEmpty(nm.getLocalPart())) { - wadlParam.setType(nm); - } - wadlParam.setName(param.value()); - - - if (!ValueConstants.DEFAULT_NONE.equals(param.defaultValue())) { - String defaultValue = cleanDefault(param.defaultValue()); - if (StringUtils.isNotEmpty(defaultValue) ) { - wadlParam.setDefault(defaultValue); - } - } - - wadlParam.setStyle(ParamStyle.QUERY); - wadlParam.setRequired(param.required()); - } else if (annotation2 instanceof PathVariable ) { - PathVariable param = (PathVariable)annotation2; - - wadlParam = new Param(); - QName nm = convertJavaToXMLType(paramType); - if (StringUtils.isNotEmpty(nm.getLocalPart())) { - wadlParam.setType(nm); - } - wadlParam.setName(param.value()); - - - wadlParam.setStyle(ParamStyle.TEMPLATE); - wadlParam.setRequired(true); - } - - return wadlParam; - } - - private Response doResponse(Set mediaTypes, ResponseStatus status) { - Response wadlResponse = new Response(); - - mediaTypes.forEach(mediaType -> { - Representation wadlRepresentation = new Representation(); - wadlRepresentation.setMediaType(mediaType.toString()); - wadlResponse.getRepresentation().add(wadlRepresentation); - }); - - wadlResponse.getStatus().add(getResposeValue(status)); - - return wadlResponse; - } - - private long getResposeValue(ResponseStatus status) { - if(status == null) { - return HttpStatus.OK.value(); - } else { - HttpStatus httpcode = status.value(); - return httpcode.value(); - } - } - - private QName convertJavaToXMLType(Class type) { - QName nm = new QName(""); - String classname = type.toString(); - classname = classname.toLowerCase(); - - if (classname.indexOf("string") >= 0) { - nm = new QName(NAMESPACE_URI, "string", "xs"); - } else if (classname.indexOf("integer") >= 0) { - nm = new QName(NAMESPACE_URI, "int", "xs"); - } else if (classname.indexOf("long") >= 0) { - nm = new QName(NAMESPACE_URI, "long", "xs"); - } - - return nm; - } - - private Resource createOrFind(String uri, Resources wadResources) { - List current = wadResources.getResource(); - for(Resource resource:current) { - if(resource.getPath().equalsIgnoreCase(uri)){ - return resource; - } - } - Resource wadlResource = new Resource(); - current.add(wadlResource); - return wadlResource; - } - - private String getBaseUrl(HttpServletRequest request) { - String requestUri = request.getRequestURI(); - int index = requestUri.lastIndexOf('/'); - requestUri = requestUri.substring(0, index); - - return request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + requestUri; - } - - private String cleanDefault(String value) { - value = value.replaceAll("\t", ""); - value = value.replaceAll("\n", ""); - return value; - } -} diff --git a/REST/web-services-spring-rest-server/src/main/resources/log4j2.xml b/REST/web-services-spring-rest-server/src/main/resources/log4j2.xml deleted file mode 100644 index ee36b97..0000000 --- a/REST/web-services-spring-rest-server/src/main/resources/log4j2.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/REST/web-services-spring-rest-server/src/main/resources/spring-configuration/mvc/rest/rest-config.xml b/REST/web-services-spring-rest-server/src/main/resources/spring-configuration/mvc/rest/rest-config.xml deleted file mode 100644 index 54b8540..0000000 --- a/REST/web-services-spring-rest-server/src/main/resources/spring-configuration/mvc/rest/rest-config.xml +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/REST/web-services-spring-rest-server/src/main/resources/spring-configuration/spring-config.xml b/REST/web-services-spring-rest-server/src/main/resources/spring-configuration/spring-config.xml deleted file mode 100644 index e50129b..0000000 --- a/REST/web-services-spring-rest-server/src/main/resources/spring-configuration/spring-config.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/REST/web-services-spring-rest-server/src/main/resources/spring-configuration/spring-doc-config.xml b/REST/web-services-spring-rest-server/src/main/resources/spring-configuration/spring-doc-config.xml deleted file mode 100644 index 773e14f..0000000 --- a/REST/web-services-spring-rest-server/src/main/resources/spring-configuration/spring-doc-config.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - diff --git a/REST/web-services-spring-rest-server/src/main/webapp/WEB-INF/web.xml b/REST/web-services-spring-rest-server/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 6c9c1d0..0000000 --- a/REST/web-services-spring-rest-server/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - Spring REST Services: example - - - - org.springframework.web.context.ContextLoaderListener - - - - - spring.profiles.active - ${environment.profile} - contextConfigLocation - - classpath*:spring-configuration/*.xml - - - - - - spring-rest - org.springframework.web.servlet.DispatcherServlet - 1 - true - - contextConfigLocation - classpath*:spring-configuration/mvc/rest/*.xml - - - - - spring-rest - - /* - - - diff --git a/REST/web-services-spring-rest-server/src/test/java/de/spring/webservices/rest/controller/CarControllerIntegrationTest.java b/REST/web-services-spring-rest-server/src/test/java/de/spring/webservices/rest/controller/CarControllerIntegrationTest.java deleted file mode 100644 index c856efb..0000000 --- a/REST/web-services-spring-rest-server/src/test/java/de/spring/webservices/rest/controller/CarControllerIntegrationTest.java +++ /dev/null @@ -1,85 +0,0 @@ -package de.spring.webservices.rest.controller; - -import static org.hamcrest.Matchers.any; -import static org.hamcrest.Matchers.is; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import de.spring.webservices.domain.Car; - - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration({ "classpath*:spring-configuration/mvc/rest/*.xml"}) -public class CarControllerIntegrationTest { - private CarController controller; - private MockMvc mockMvc; - - @Before - public void setup() { - controller = new CarController(); - mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); - } - - @Test - public void testWhenGetAllCarsThenRetrieveJsonValues() throws Exception { - mockMvc.perform(get("/api/cars/") - .accept(MediaType.APPLICATION_JSON_UTF8)) - - .andExpect(status().isOk()) - .andExpect(jsonPath("$[0].id", any(Integer.class))) - .andExpect(jsonPath("$[0].content", is("Car: 1"))) - .andExpect(jsonPath("$[1].content", is("Car: 2"))) - .andExpect(jsonPath("$[1].id", any(Integer.class))) - .andExpect(jsonPath("$[2].content", is("Car: 3"))) - .andExpect(jsonPath("$[2].id", any(Integer.class))) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)); - } - - @Test - public void testWhenGetOneCarThenRetrieveJsonValue() throws Exception { - mockMvc.perform(get("/api/cars/{id}", 1L) - .accept(MediaType.APPLICATION_JSON_UTF8)) - - .andExpect(status().isOk()) - .andExpect(jsonPath("id", any(Integer.class))) - .andExpect(jsonPath("content", is("Car: 1"))) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)); - } - - @Test - public void testWhenCreateNewCarThenRetrieveJsonValue() throws Exception { - Car car = new Car(2L, "nothing"); - mockMvc.perform(post("/api/cars/") - .contentType(MediaType.APPLICATION_JSON_UTF8) - .content(asJsonString(car)) - .accept(MediaType.APPLICATION_JSON_UTF8)) - - .andExpect(status().isCreated()) - .andExpect(jsonPath("id", any(Integer.class))) - .andExpect(jsonPath("content", is("Car: 1"))) - .andExpect(header().string(HttpHeaders.LOCATION, "/api/cars/1")) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)); - } - - private static String asJsonString(final Object obj) throws JsonProcessingException { - final ObjectMapper mapper = new ObjectMapper(); - return mapper.writeValueAsString(obj); - } -} diff --git a/REST/web-services-spring-rest/pom.xml b/REST/web-services-spring-rest/pom.xml deleted file mode 100644 index e8ce3dc..0000000 --- a/REST/web-services-spring-rest/pom.xml +++ /dev/null @@ -1,265 +0,0 @@ - - 4.0.0 - de.spring.webservices - web-services-spring-rest - pom - 1.0-SNAPSHOT - web-services-spring-rest - http://gumartinm.name - Web Services Spring Framework - - Gustavo Martin Morcuende - http://www.gumartinm.name - - - scm:git:http://git.gumartinm.name/SpringWebServicesForFun - http://git.gumartinm.name/SpringWebServicesForFun - - - UTF-8 - UTF-8 - 4.2.4.RELEASE - - - - release - - release - - - true - - - - - - - org.apache.logging.log4j - log4j-slf4j-impl - 2.3 - - - - - org.apache.logging.log4j - log4j-core - 2.3 - - - - - org.slf4j - jcl-over-slf4j - 1.7.12 - - - - - cglib - cglib - 2.2.2 - - - - - - org.springframework - spring-core - ${spring.version} - - - - commons-logging - commons-logging - - - - - org.springframework - spring-webmvc - ${spring.version} - - - - commons-logging - commons-logging - - - - - - - javax.servlet - javax.servlet-api - 4.0.0-b01 - provided - - - - - com.fasterxml.jackson.core - jackson-databind - 2.6.4 - - - - - - javax.validation - validation-api - 1.1.0.Final - - - org.hibernate - hibernate-validator - 5.2.2.Final - - - - - junit - junit - 4.12 - test - - - org.springframework - spring-test - ${spring.version} - test - - - org.mockito - mockito-core - 2.0.11-beta - test - - - - - org.hamcrest - hamcrest-core - 1.3 - test - - - org.hamcrest - hamcrest-library - 1.3 - test - - - com.jayway.jsonpath - json-path - 2.1.0 - test - - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.18.1 - - - org.apache.maven.plugins - maven-failsafe-plugin - 2.18.1 - - - org.apache.maven.plugins - maven-war-plugin - 2.6 - - - - true - src/main/webapp - - WEB-INF/web.xml - - - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.3 - - 1.8 - 1.8 - ${project.build.sourceEncoding} - - - - org.apache.maven.plugins - maven-resources-plugin - 2.7 - - ${project.build.sourceEncoding} - - - - org.apache.maven.plugins - maven-jar-plugin - 2.4 - - - - ${project.description} - ${project.version} - ${project.organization.name} - ${project.description} - ${project.version} - ${project.organization.name} - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - **/*IntegrationTest.java - - - - - org.apache.maven.plugins - maven-failsafe-plugin - - - - integration-test - verify - - - - - - **/*IntegrationTest.java - - - - - - \ No newline at end of file