I have a Spring Controller that is being invoked via an HTTP POST from the GAE Queue Scheduler.
#Controller
#RequestMapping(value = RSSPoller.RSS_POLLER_URL)
public class RSSPoller implements Serializable {
private static final long serialVersionUID = -4925178778477404709L;
public static final String RSS_POLLER_URL = "/rsspoller";
#RequestMapping(method = RequestMethod.POST)
#ResponseStatus(HttpStatus.OK)
public void pollAndProcessRssFeed() throws ServiceException {
try {
// do some stuff
}
catch(Exception e) {
throw new ServiceException("Can't process RSS feed because", e);
}
}
}
However when it gets invoked, the response code is 500 with a Critical log message of
Uncaught exception from servlet
java.lang.RuntimeException: java.io.NotSerializableException: <some spring class that changes all the time, but does not implement java.io.Serializable>
The same log message shows up in the logs with a Warning level as well.
I get similar warning messages (but not critical) in my logs when I invoke other Spring Controllers that either render a web page (GET), or returns some XML data (essentially RPC invokes which use HTTP POST). When I do an HTTP GET/POST to those URLs, the response code is 200 and the output is correct (and I ignore the warning message in the logs).
That leads me to two questions:
Why do I get the Critical error message/HTTP 500 for the POST from the queue, but not the GET/POST to other Spring Controllers in my app?
How can I trap the exception and essentially discard it; as to my purposes the task is complete.
I can post the full exception log if it's of use; for brevity I've omitted it.
you should make your <some spring class that changes all the time, but does not implement java.io.Serializable> just Serializable (not only Controller). Me at least helped it.
Related
I'm writing a Web application that makes downstream calls using RestTemplate. If the underlying service returns a 401 Unauthorized, I want to also return a 401 to the calling application; the default behavior is to return a 500. I want to keep the default Spring Boot error response as provided by BasicErrorController; the only change I want is to set the status code.
In custom exceptions, I'd just annotate the exception class with #ResponseStatus, but I can't do that here because HttpClientErrorException.Unauthorized is provided by Spring. I tried two approaches with #ControllerAdvice:
#ExceptionHandler(HttpClientErrorException.Unauthorized.class)
#ResponseStatus(UNAUTHORIZED)
public void returnsEmptyBody(HttpClientErrorException.Unauthorized ex) {
}
#ExceptionHandler(HttpClientErrorException.Unauthorized.class)
#ResponseStatus(UNAUTHORIZED)
public void doesNotUseBasicErrorController(HttpClientErrorException.Unauthorized ex) {
throw new RuntimeException(ex);
}
How can I configure MVC to continue to use all of the built-in Boot error handling except for explicitly overriding the status code?
The below code works for me -- in an app consisting of a #RestController whose one method consisted of throw new HttpClientException(HttpStatus.UNAUTHORIZED), running on an embedded Tomcat. If you're running on a non-embedded Tomcat (or, I suspect, on an embedded non-Tomcat) odds are you'll have to do something at least somewhat different, but I hope this answer is at least somewhat helpful anyway.
#ControllerAdvice
public class Advisor {
#ExceptionHandler(HttpClientException.class)
public String handleUnauthorizedFromApi(HttpClientException ex, HttpServletRequest req) {
if (/* ex instanceof HttpClientException.Unauthorized or whatever */) {
req.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, 401);
}
return "forward:/error";
}
}
Explanation: when a HttpClientException is thrown while we're processing request X (in an embedded servlet), what normally happens is that it bubbles all the way up to some org.apache class. (I might fire the debugger up again and work out which one, but this is a pretty high-level explanation so it doesn't matter much.) That class then sends request X back to the application, except this time the request goes to "/error", not to wherever it was originally going. In a Spring Boot app (as long as you don't turn some autoconfiguration off), that means that request X is ultimately processed by some method in BasicErrorController.
OK, so why does this whole system send a 500 to the client unless we do something? Because that org.apache class mentioned above sets something on request X which says "processing this went wrong". It is right to do so: processing request X did, after all, result in an exception which the servlet container had to catch. As far as the container is concerned, the app messed up.
So we want to do a couple of things. First, we want the servlet container to not think we messed up. We achieve this by telling Spring to catch the exception before it reaches the container, ie by writing an #ExceptionHandler method. Second, we want the request to go to "/error" even though we caught the exception. We achieve this by the simple method of sending it there ourselves, via a forward. Third, we want the BasicErrorController to set the correct status and message on the response it sends. It turns out that BasicErrorController (working in tandem with its immediate superclass) looks at an attribute on the request to determine what status code to send to the client. (Figuring this out requires reading the class's source code, but that source code is on github and perfectly readable.) We therefore set that attribute.
EDIT: I got a bit carried away writing this and forgot to mention that I don't think using this code is good practice. It ties you to some implementation details of BasicErrorController, and it's just not the way that the Boot classes are expected to be used. Spring Boot generally assumes that you want it to handle your error completely or not at all; this is a reasonable assumption, too, since piecemeal error handling is generally not a great idea. My recommendation to you -- even if the code above (or something like it) does wind up working -- is to write an #ExceptionHandler that handles the error completely, meaning it sets both status and response body and doesn't forward to anything.
You can customize the error handler of the RestTemplate to throw your custom exception, and then handle that exception with the #ControllerAdvice as you mentioned.
Something like this:
#Configuration
public class RestConfig {
#Bean
public RestTemplate restTemplate(){
// Build rest template
RestTemplate res = new RestTemplate();
res.setErrorHandler(new MyResponseErrorHandler());
return res;
}
private class MyResponseErrorHandler extends DefaultResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse response) throws IOException {
if (HttpStatus.UNAUTHORIZED.equals(response.getStatusCode())) {
// Throw your custom exception here
}
}
}
}
I want to know what is best practice to preserve error messages when calling several micro services that is chained: I have an angular front end that calls a back end rest service which calls another rest service which calls another 3rd party service.
The 3rd party service is somewhat unreliable. And I want the response from that service to be propagated to my front end.
So to make it easier for the sake of demo’ing the problem.
I have a control class in downstream project (separate micro-service/application)
#RestController
#RequestMapping("/my-down-stream-service")
public class MyController {
#RequestMapping(value = "my-method")
public MyCustomResponse method1() {
//Some complex logic that catch exceptions and propogates a nice little message
throw new RuntimeException(“This is my exception that indicates what the response is to my 3rd party service”);
}
}
On the other micro-service calling the service above I have a restTemplate making the call to the above service
public MyResponse doIt() {
try {
restTemplate.postForEntity(“MyUrl…”, req, MyResponse.class);
} catch (final HttpStatusCodeException ex) {
//If I add a break point and inspect the exception here
}
}
I can see it is a 500 internal exception that gets send to the front end.
If I go and get the ex.getResponseBodyAsString() I get back a JSON map with the actual detail of the exception.
{
"timestamp": "2020-05-06T22:17:08.401+0200",
"status": 500,
"error": "Internal Server Error",
"exception": "java.lang.RuntimeException",
"message": "This is my exception that indicates what the response is to my 3rd party service",
"path": "…"
}
And I can convert this into a map and get the message portion and construct a new exception and throw that
new ObjectMapper().readValue(ex.getResponseBodyAsString(), HashMap.class).get("message")
But this seems like a lot of work that needs to be implemented where ever I need this.
Is there a better way of doing this?
I also tried creating my own HttpStatus - Like a 550 with my "Own custom message". But you cannot set the message for the HttpStatus code dynamically aka at Runtime. Not even sure if this is the correct venture or path to go down.
My solution in the end based on Amit's suggestion
I finally ended up creating a custom class that extends springs ResponseEntityExceptionHandler. If this is on the class path of your springboot app it will intercept the exception before returning it from the controller. I also created my own exception. Reason being this way if I want my functionality to trigger I fire my own exception and everyone else can still follow the normal way. It can be changed at any time.
Also on the client side I had to cast the exception's getBody() JSON to my exception. But I didn't knew if it was my exception to start of with. So I also added some HTTP header. And on the client side I check if that header is present then I know the body is my exception and I could comfortable convert the JSON to my exception.
#ControllerAdvice
public class MyRestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = {MyCustomException.class})
protected ResponseEntity<Object> handleConflict(final MyCustomException ex, final HttpServletResponse response) {
if (!response.containsHeader("MYTAG")) {
response.addHeader("EX_TYPE", "MYTAG");
}
//here you can go wild as to what type of or just the normal 500
//return ResponseEntity.status(ex.getHttpStatus()).body(ex); // 500 internal exception
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex);
}
}
If I were you, I would like to create a controller advice to handle all kind of exceptions. Then I would like to create a ErrorMessage class which will have custom errorCode, errorMessage fields as per requirements. From this controller advice, for any kind of exceptions occurred in application, it will create an instance of ErrorMessage with details like errorCode and errorMessage and wrap into ResponseEntity object (with HTTP status) and return to the other microservices.
At consumer end check the response status and act accordingly.
I think the answer you are looking for is creating an implementation of ExceptionMapper. The interface is designed to handle java exceptions that map to Response.
In your case, if the 3rd part throws an exception which is handled by the ExceptionMapper implementation you can access the error message and return that in the response.
public class ServiceExceptionMapper implements ExceptionMapper<ServiceException>
{
/**
* {#inheritDoc}
*/
#Override
public Response toResponse(ServiceException exception)
{
//grab the message from the exception and return it in the response
}
I'm writing a non-blocking Spring Rest controller. My client should send a request and doesn't care for the response and doesn't need to wait.
This is my server code:
#RestController
#EnableAsync
public class testController {
#RequestMapping(value = "test", method = RequestMethod.GET)
public ResponseEntity<String> test() throws InterruptedException {
timeConsumingMethod();
System.out.println("I'm should be first");
return new ResponseEntity<String>("the server is processing your request", HttpStatus.OK);
}
#Async
private void timeConsumingMethod() throws InterruptedException {
Thread.sleep(1000*5);
System.out.println("I'm should be second!");
}
However, When I call http://localhost:8181/test using(POSTMAN, Chrome, etc...)
I get the following on the server log:
I'm should be second!
I'm should be first
AND only after waiting 5 seconds my browser shows:
the server is processing your request
Is that the correct way for a "send and forget" Behavior?
According to the doc page the #EnableAsync should be added on configuration class.
Enables Spring's asynchronous method execution capability, similar to
functionality found in Spring's XML namespace.
To be used on #Configuration classes as follows, where MyAsyncBean is
a user-defined type with one or more methods annotated with either
Spring's #Async annotation, the EJB 3.1 #javax.ejb.Asynchronous
annotation, or any custom annotation specified via the annotation()
attribute.
why don't you use this:
https://www.baeldung.com/spring-webclient-resttemplate
Webflux client seems to do the same. I was searching for a similar solution where 1 microservice calls multiple microservices async and this fits the model
I'm running a Spring Boot 1.2.1 application in standalone Tomcat.
I have two controller mappings, which both for simplicity always throw an exception. The first one is for a GET request and it returns a String for the view name:
#RequestMapping(value = {
"/index"
}, method = RequestMethod.GET)
public String init() throws MyRequestProcessingException {
if (true) {
throw new MyRequestProcessingException(
"Something went wrong processing request");
}
return "init";
}
This is the exception definition:
#ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR)
public class MyRequestProcessingException extends Exception {
public MyRequestProcessingException(String message) {
super(message);
}
}
In embedded Tomcat as well as in standalone Tomcat, trying to access /index always results in a 500 with some JSON error data being returned to the client.
My controller has another method which accepts a POST and returns a DeferredResult:
#RequestMapping(value = "html/start", method = RequestMethod.POST, consumes = APPLICATION_FORM_URLENCODED)
public DeferredResult<String> start(final HttpServletResponse response,
#Valid #ModelAttribute final InitialisationStartAttributes model,
final SessionData sessionExisting) throws MyRequestProcessingException {
final DeferredResult<String> finalResult = new DeferredResult<>(5000);
// Just return an error, so we can test
if (true) {
finalResult.setErrorResult(new MyRequestProcessingException(
"Something went wrong processing request"));
}
return finalResult;
}
In embedded Tomcat, a POST to /html/start returns a 500 with some JSON data in the response body, just like the other request method. However, when I try to reproduce this behaviour using a standalone Tomcat instance, I always get a 200 response with no response body.
I'm using Tomcat 8 in embedded and Tomcat 7.0.52 standalone, but I've also tried with standalone Tomcat 8 and it doesn't make a difference.
My application is deployed in the root application context by modifying /etc/tomcat/Catalina/localhost/ROOT.xml.
EDIT: I've done a bit more testing, and it does seem that DeferredResult is the culprit. I have yet to override handleErrorResult() to see what happens. I'm a bit surprised though, because I don't recall seeing anything in the documentation about the difference between returning a DeferredResult in embedded vs standalone Tomcat.
If you throw an exception in a #RequestMapping and don't handle it anywhere, then the response status is not set before you hit the ErrorPageFilter. If you want to map status to error path in your customizer, you need to handle the error and get the status set in the filter chain somewhere (e.g. with an #ExceptionHandler or by using an Exception with a #ResponseStatus). Another way to get your custom error page to render would be to map exceptions instead of (or as well as) the HttpStatus, using new ErrorPage(Exception.class, "/html/error").
This behaviour was due to a bug in Spring Boot's ErrorPageFilter; see bug report on GitHub. It was fixed in Spring Boot 1.2.3.
I had an issue with Apache Camel that timeouts for after 20 seconds.
I was getting timeout errors after 20 seconds. I have a custom processor the implements Processor. I injected a DAO and when finding the data within the custom processor it took longer to find the data on the Apache Camel side and it timed out. If I run the same code without Apache Camel it runs instantly. By doing a SELECT inside the CustomProcessor it took longer to find the data.
The memory reference for the DAO are the same, so in the test the data is fetched immediately and the CustomProcessor hangs for 20 seconds before the data is receieved and it throws an Exception.
I figured out that I need to add ?disableReplyTo=true as below:
source=activemq:queue:notification?disableReplyTo=true
This fixed the timeout issue, but nothing was being added to the queue. When I request the body of the message it returns the input XML that was sent. By default, by adding ?disableReplyTo=true sets the ExchangePattern to InOnly. Is there any way of overriding this to ExchangePattern.OutOnly? I want the message to be sent to the queue and also I want to get the body of the message that is set through producerTemplate.requestBody().
What I noted that going from one processor to another the other processor can access the exchange.setOut() as as exchange.getIn().
I have located the code on Githib: https://github.com/rajivj2/example2
I tried something like this:
public class ContentEnricherProcessor implements Processor {
#Resource
private StatusDAO statusDAO;
private Logger logger;
public ContentEnricherProcessor(StatusDAO statusDAO) {
this.statusDAO = statusDAO;
logger = LoggerFactory.getLogger(ContentEnricherProcessor.class);
}
public void process(Exchange exchange) throws Exception {
exchange.setPattern(ExchangePattern.OutOnly); // by doing producerTemplate.requestBody() this does not work
Message message = exchange.getIn();
Entity entity = (Entity) message.getBody();
Status status = process(entity);
message.setBody(status);
exchange.setOut(message);
}
I hope you can help.