I have a class extends HttpServlet contains following two methods. When a client makes a request to web app running on a Tomcat server, http request will be handled via this class however if doSomething method throws RunTimeException how it gets handled based on below example?
protected void doPost(HttpServletRequest reqst, HttpServletResponse resp) {
doSomething();
}
private void doSomething() {
if (false) {
throw new RuntimeException("exception caught calling doSomething method");
}
else {
...
}
}
[update]
Can I add try/catch block inside of doPost where doSomething() is invoked?
All exceptions that reach servlet container code (i.e., those thrown or re-thrown by the doPost() method) will get handled such that Tomcat generates a 500 response, sets Content-Type to text/html, and generates a full HTML page in the body which says that an internal server error has occured, attaching the name and stack trace of the exception.
RE: your [Update]: yes, you can add a try-catch there. In fact, any reasonable implementation will include it. You want your application to dictate what response is sent to your client, not Tomcat or any other container.
Related
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 have web service where i call methods of business service. In one of the methods some kind of exception with appropriate message is thrown. In soap I get exception type and its message (everything is OK). But In log file a bunch of stack trace is written (with detail information). I don't need that. All I need in log file are exception type and its message(like in soap response). But I wouldn't like to change log properties, because of another exceptions must be printed out.
So question is How can I cut down stack trace in log file ?
I've made efforts to solve this problem with #Interceptors annotation bundled on the web method. There my web service method and Interceptor class are shown. Exception is thrown in getLbsLocationForService method .
#WebMethod
#Override
#Interceptors(InterceptionHandleUtil.class)
public LbsLocation getLbsLocationService(#WebParam(name="ms")String ms, #WebParam(name="serID")Long ser) throws ScAccViolation, LbsException {
return serviceProcesses.getLbsLocationForService(ms, ser);
}
Interceptor class :
#Interceptor
public class InterceptionHandleUtil{
#AroundInvoke
public Object intercept(InvocationContext invocationContext) throws Exception {
/*What can I do here to write down in log file just exception type and message*/
return invocationContext.proceed();
}
}
When something fails on the server side because the database and the application are out of sync instead of getting an error and the application crashing spring/tomcat seems to swallow the exception and pretend nothing has happened.
Call me crazy but if the program fails catastrophically I want it to actually fail catastrophically! Is there anyway to switch this behaviour off? It's really slowing development down when the server pretends that everything is fine when it's just thrown up into the logs.
If this isn't the spring/tomcat default then what else might be causing it?
We are using a boatload of libraries and frameworks unfortunately. Spring would be the usual suspect but it could be something else.
Update
It's a sql server database which we are connecting to using SqlServerDataSource. Hibernate is in use in some parts of the project but is used to query the database at login time. On the client side we are using extjs and we are also using ExtDirectSpring to annotate methods for the client side to talk to. To translate the data going across the wire there's Jackson, which then gets wrapped by the extdirect json handler.
There's some AOP stuff going on thats to do with logging exceptions but deleting that code results in the same behaviour.
Further update
Ok its not a good idea to let your sever crash! See my answer below for my proposed middle ground.
If you really want to do that (but IMHO you should not ...) you can use a filter that will block the application once it let an uncaught exception go. It could be something like :
public class CrashFilter implements Filter {
private boolean crashed = false;
private String msg = "Major problem : application stopped";
#Override
public void doFilter(ServletRequest sr, ServletResponse sr1, FilterChain fc) throws IOException, ServletException {
if (crashed) {
HttpServletResponse resp = (HttpServletResponse) sr1;
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg);
return;
}
try {
fc.doFilter(sr, sr1);
}
catch (Exception ex) {
crashed = true;
throw new ServletException(ex);
}
}
// init and destroy omitted from brevity
}
Ok so I did this in the end. I've basically used the ideas above but thought there was enough extra to post my own answer.
It turns out you really shouldn't do this as other people suggested and I've added a bit at the bottom to say why!
Here's my filter:
public class FailOnErrorFilter implements Filter
{
#Override
public void init(FilterConfig config) throws ServletException
{
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException
{
try {
filterChain.doFilter(request, response);
}
catch (Exception exception) {
System.exit(1);
}
}
#Override
public void destroy()
{
}
}
To get this working you have to modify the web.xml:
<filter>
<filter-name>failingFilter</filter-name>
<filter-class>fullyQualified.FailOnErrorFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>failingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
The top one defines the filter + the second one says use it everywhere.
ExtDirectSpring
After doing the above I realised that ExtDirectSpring was a further culprit. It's default behaviour is to swallow all exceptions in server side methods.
I was worried I was going to have to patch the library but thankfully someone else had already complained about this and it got fixed in 1.3.6. Initially I tried upgrading to the latest version but it broke a load of code! What a great library. Anyway 1.3.6 added in the ability to switch off the suppression of errors by doing the following:
//this is to switch off blanket switching of exceptions in extdirect spring
#Component
public class NoExceptionHandling implements RouterExceptionHandler
{
#Override
public Object handleException(MethodInfo methodInfo, BaseResponse baseResponse, Exception e, HttpServletRequest httpServletRequest)
{
throw new RuntimeException(e);
}
}
As the name suggests extdirectspring uses spring and so doesn't make its dependencies obvious to calling code, however if you go digging (its on github). You'll see in RouterController it calls the following method in the catch
private Object handleException(MethodInfo methodInfo, BaseResponse response, Exception e, HttpServletRequest request) {
return configurationService.getRouterExceptionHandler().handleException(methodInfo, response, e, request);
}
Where router controller does this:
#Autowired(required = false)
private RouterExceptionHandler routerExceptionHandler;
public RouterExceptionHandler getRouterExceptionHandler() {
return routerExceptionHandler;
}
It sets up a default one if you dont provide one.
Update - why you shouldn't do this
It turns out you really shouldn't call System.exit in a tomcat application. Not only does it bring down your application it also causes the server to exit. This brings down any other applications running aswell!
It's also not appropriate for a number of other reasons:
if the first in a series of tests throws an exception then all subsequent tests will fail
Its time consuming restarting the server and you have to be the person who breaks it to see the exception
if you are running a manual test deployment on a seperate machine then you have to restart the server if something caused a problem somewhere.
Likewise:
In production it will take everyone's application down and most users wont be in a position to restart the server.
What I'm doing instead
The errors were already being written the the tomcat logs + the database.
In debug we now also going to redirect to an error page with the stacktrace
In production we are going to just redirect to a 'something went wrong' page. We're also going to set up an email service that notifies us of exceptions.
For UI/selenium tests - it'll work the same as debug
For headless Js tests the server rejects subsequent requests until the next test resets the error state of the server
Just to make things more complicated unsurprisingly the original webapp is too flaky to not mask errors so I've kept the old error suppression in place for that as we're not actively developing/fixing it at the moment.
I understand that a Jersey-based web service is able to associate exceptions thrown by service methods to desired HTTP return codes (here). Now, is there any chance to make the client generate exactly the same exception that was generated by the service method? I mean, if the server side throws MySpecificException, is there a way to store such information (i.e., the FQN of the exception class) in the HTTP response (automatically, I don't want to turn to methods that build the response explicitly, I want them to return POJOs or void), so that the client can use it to re-throw the same exception?
REST does not specify exception as a response and thus there's no straightforward way to do this (this is not RPC).
However, you can introduce your own convention. For example:
On the provider side you could define ForbiddenException:
public class ForbiddenException extends WebApplicationException {
public ForbiddenException(String code, String readableMessage) {
super(Response.status(Status.FORBIDDEN).entity(new ForbiddenEntity(code, readableMessage)).build());
}
}
(You should probably compose response in ExceptionMapper instead of exception itself, but this is just for demonstration purposes).
And on the consumer side - ClientFilter:
public class ForbiddenExceptionClientFilter extends ClientFilter {
#Override
public ClientResponse handle(ClientRequest cr) throws ClientHandlerException {
ClientResponse response = getNext().handle(cr);
if (response.getStatus() == Status.FORBIDDEN.getStatusCode()) {
ForbiddenEntity reason = response.getEntity(ForbiddenEntity.class);
throw new RemoteResourceForbiddenException(reason.getCode(), reason.getReadableMessage());
}
return response;
}
}
This works as long as server complies with the convention and client uses the client filter.
Please note, this is not "exactly the same" exception - stacktrace is not transferred, however this is the right thing to do as it does not make any sense in client application. If you need stacktrace - it should be printed to logs using ExceptionMapper on server side.
Server errors result in HTTP 500-responses to the client with a generic error message ("The server encountered an error..."). Is there any way to intercept this message and write a custom one?
I'm would like to have a way to uniquely identify a server error from the client. If I could for instance generate a GUID which I logged server-side upon a server error and then send that ID to the client, that would make it easy to search for that particular exception in the log at any point later in time.
I do realize that server errors are generated by exceptions in the code, so I'm looking for some kind of catch all exception hook in the app engine API. Of course, if such a hook exists, and the code which executes here generates a second exception, it would have to default to the general 500-error again.
I'm using the Java API for GAE
For GAE generated errors you can configure a custom error page. For errors generated by your code you should use a catch-all wrapper inside a first servlet filter.
I ended up coding a Servlet Filter by following the answer in this SO question. The filter wraps the doFilter() call in a general try-catch block and creates a reference number for the client while logging it at the server. I think this little snippet might be useful for others out there:
public class ExceptionFilter implements Filter {
private FilterConfig filterConfig;
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
try {
filterChain.doFilter(request, response);
}
catch (Exception ex) {
String errorId = UUID.randomUUID().toString();
Mylog.e("Server error " + errorId); // Use whatever logging mechanizm you prefer
String clientResponse = "Server error. Reference no: " + errorId;
((HttpServletResponse) response).setStatus(500);
response.getWriter().write(clientResponse);
}
}
public FilterConfig getFilterConfig() {
return filterConfig;
}
public void init(FilterConfig filterConfig) {
this.filterConfig = filterConfig;
}
public void destroy() {}
}
You also need to configure web.xml like this (goes somewhere under the <web app> tag):
<filter>
<filter-name>ExceptionFilter</filter-name>
<filter-class>your.package.ExceptionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ExceptionFilter</filter-name>
<servlet-name>Your Servlet Name As Defined In servlet-mapping</servlet-name>
</filter-mapping>
You didn't mention if your using python or java. Python error display https://developers.google.com/appengine/docs/python/config/appconfig#Custom_Error_Responses
Note these are just static pages that are shown in the event of any uncaught errors.
You can try and catch these errors in your main handler (I am talking about python), but you can't always. For instance you maybe able to catch a DeadlineExceededError some times, and you may have a tiny bit of time to emit a log, or a redirect (maybe to the same page to try again or to your own static page, with an arg with the GUID you mentioned, then have javascript render it some useful way) but often that won't work. So it very much depends on the nature of the error.