1 package de.spring.webservices.rest.controller;
4 import java.util.concurrent.Callable;
6 import javax.inject.Inject;
8 import org.slf4j.Logger;
9 import org.slf4j.LoggerFactory;
10 import org.springframework.data.domain.Page;
11 import org.springframework.data.domain.PageRequest;
12 import org.springframework.http.HttpHeaders;
13 import org.springframework.http.HttpStatus;
14 import org.springframework.http.MediaType;
15 import org.springframework.http.ResponseEntity;
16 import org.springframework.web.bind.annotation.PathVariable;
17 import org.springframework.web.bind.annotation.RequestBody;
18 import org.springframework.web.bind.annotation.RequestHeader;
19 import org.springframework.web.bind.annotation.RequestMapping;
20 import org.springframework.web.bind.annotation.RequestMethod;
21 import org.springframework.web.bind.annotation.RequestParam;
22 import org.springframework.web.bind.annotation.ResponseStatus;
23 import org.springframework.web.bind.annotation.RestController;
24 import org.springframework.web.context.request.async.WebAsyncTask;
26 import de.spring.webservices.domain.Car;
27 import de.spring.webservices.rest.business.service.AwesomeBusinessLogic;
30 @RequestMapping("/api/callable/cars/")
31 public class CallableCarController {
32 private static final Logger LOGGER = LoggerFactory.getLogger(DeferrableCarController.class);
33 private static final int PAGE = 2;
34 private static final int PAGE_SIZE = 10;
36 // With no value, we depend on the Tomcat/Jboss/Jetty/etc timeout value for asynchronous requests.
37 // Spring will answer after 60 secs with an empty response (by default) and HTTP 503 status (by default) when timeout.
38 private static final long ASYNC_TIMEOUT = 60000; /* milliseconds */
43 * WHEN EXCEPTION FROM CALLABLE, Spring WILL TRIGGER THE Spring Exception Handler AS YOU KNOW IT.
44 * See: https://spring.io/blog/2012/05/10/spring-mvc-3-2-preview-making-a-controller-method-asynchronous/
46 * When an Exception is raised by a Callable, it is handled through the
47 * HandlerExceptionResolver mechanism just like exceptions raised by any
48 * other controller method. The more detailed explanation is that the
49 * exception is caught and saved, and the request is dispatched to the
50 * Servlet container where processing resumes and the
51 * HandlerExceptionResolver chain invoked. This also means
52 * that @ExceptionHandler methods will be invoked as usual.
55 * SO, YOU COULD HOOK UP THE HANDLER AND RETURN YOUR CUSTOM MESSAGESS (as
61 private final AwesomeBusinessLogic awesomeBusinessLogic;
64 public CallableCarController(AwesomeBusinessLogic awesomeBusinessLogic) {
65 this.awesomeBusinessLogic = awesomeBusinessLogic;
68 @RequestMapping(produces = { MediaType.APPLICATION_JSON_UTF8_VALUE }, method = RequestMethod.GET)
69 @ResponseStatus(HttpStatus.OK)
70 public Callable<Page<Car>> cars() {
72 // Relying on the timeout given by Tomcat/Jboss/Jetty/etc. WebAsyncTask if you want to control your timeout (see below)
74 // return new Callable<Page<Car>>() {
76 // public Page<Car> call() throws Exception {
77 // return awesomeBusinessLogic.findAll(new PageRequest(PAGE, PAGE_SIZE));
82 return () -> awesomeBusinessLogic.findAll(new PageRequest(PAGE, PAGE_SIZE));
86 @RequestMapping(value = "{id}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.GET)
87 @ResponseStatus(HttpStatus.OK)
88 public WebAsyncTask<Car> car(@RequestHeader(value = "MY_HEADER", required = false) String specialHeader,
89 @PathVariable("id") long id,
90 @RequestParam Map<String, String> params,
91 @RequestParam(value = "wheel", required = false) String[] wheelParams) {
93 if (specialHeader != null) {
94 LOGGER.info("SPECIAL HEADER: " + specialHeader);
97 if (params.get("mirror") != null) {
98 LOGGER.info("MIRROR: " + params.get("mirror"));
101 if (params.get("window") != null) {
102 LOGGER.info("WINDOW: " + params.get("window"));
105 if (wheelParams != null) {
106 for(String wheel : wheelParams) {
112 // If you want to control stuff like timeout you must use WebAsyncTask + Callable :)
114 // Callable<Car> callable = new Callable<Car>() {
116 // public Car call() throws Exception {
117 // return awesomeBusinessLogic.findById(id);
120 // return new WebAsyncTask<>(ASYNC_TIMEOUT, callable);
123 return new WebAsyncTask<>(ASYNC_TIMEOUT, () -> awesomeBusinessLogic.findById(id));
126 @RequestMapping(consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,
127 produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.POST)
128 @ResponseStatus(HttpStatus.CREATED)
129 public WebAsyncTask<ResponseEntity<Car>> create(@RequestBody Car car) {
131 // If you want to control stuff like timeout you must use WebAsyncTask + Callable :)
133 // Callable<ResponseEntity<Car>> callable = new Callable<ResponseEntity<Car>>() {
135 // public ResponseEntity<Car> call() throws Exception {
136 // Car createdCar = awesomeBusinessLogic.create(car);
138 // HttpHeaders headers = new HttpHeaders();
139 // headers.add(HttpHeaders.LOCATION, "/api/cars/" + createdCar.getId());
140 // return new ResponseEntity<>(createdCar, headers, HttpStatus.CREATED);
145 Callable<ResponseEntity<Car>> callable = () -> {
146 Car createdCar = awesomeBusinessLogic.create(car);
148 HttpHeaders headers = new HttpHeaders();
149 headers.add(HttpHeaders.LOCATION, "/api/cars/" + createdCar.getId());
150 return new ResponseEntity<>(createdCar, headers, HttpStatus.CREATED);
153 return new WebAsyncTask<>(ASYNC_TIMEOUT, callable);