Dropwizard deserialising custom exception as JSON? - java

I have created a new exception class in my Dropwizard service that extends BadRequestException.
public class CustomBadRequestException extends BadRequestException {
private static final long serialVersionUID = 1L;
private List<ValidationFailureDto> validationFailures;
public CustomBadRequestException() {
super();
}
public CustomBadRequestException(final List<ValidationFailureDto> validationFailures) {
super();
this.validationFailures = validationFailures;
}
#ApiModelProperty(value = "List of validationFailures")
public List<ValidationFailureDto> getValidationFailures() {
return validationFailures;
}
}
When I throw that exception at first I was only getting back the deserialised BadRequestException, minus the additional property (validationFailures)
{
code: "400",
message: "Bad request"
}
This is because Dropwizard's internals have a default exception mapper that allows Jetty/Jackson to understand domain exceptions and how to send the appropriate HTTP response.
To overcome this you can implement your own ExceptionMapper class and register it with Dropwizard.
public class CustomBadRequestExceptionMapper implements ExceptionMapper<SamplePackOrderBadRequestException> {
/**
* Allows jackson to deserialise custom exceptions and its properties to JSON response
*
* #param exception exception
* #return response object
*/
#Override
public Response toResponse(final SamplePackOrderBadRequestException exception) {
if (exception instanceof SamplePackOrderBadRequestException) {
SamplePackOrderBadRequestException samplePackOrderBadRequestException
= (SamplePackOrderBadRequestException) exception;
return Response
.status(400)
.entity(samplePackOrderBadRequestException)
.build();
}
return Response.status(400).build();
}
}
However this issue with this is that it deserializes super (Throwable), so you get every single inherited property added in the response which I do not want.
To combat this I tried adding Jackson annotations like so:
#JsonIgnoreProperties(value = "stackTrace")
This is not an optimal solution as there are several properties other than stackTrace that I will need to ignore.
So to summarise, how can I get Dropwizard to properly deserialize my CustomException class without all the additional clutter that I do not need?

I think the easier option is to transform exception to a Error bean and return it as shown below.
public class CustomBadRequestExceptionMapper implements ExceptionMapper<SamplePackOrderBadRequestException> {
#Override
public Response toResponse(final SamplePackOrderBadRequestException exception) {
if (exception instanceof SamplePackOrderBadRequestException) {
SamplePackOrderBadRequestException ex
= (SamplePackOrderBadRequestException) exception;
return Response
.status(400)
.entity(new ErrorBean(400,ex.getMessage,ex.getgetValidationFailures()))
.build();
}
return Response.status(400).build();
}
}
And ErrorBean.java
public static class ErrorBean{
private int code;
private String message;
private List<ValidationFailureDto> failures;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public List<ValidationFailureDto> getFailures() {
return failures;
}
public void setFailures(List<ValidationFailureDto> failures) {
this.failures = failures;
}
}

Related

How to create a polymorphic structure that can work for both success and error responses in Java?

I'm working on a class that will get a list of strings and process them asynchronously using CompletableFutures. Each string is processed by invoking another class that will perform several operations and return a response or throw an exception if there is an error.
I would like to aggregate the responses that I get, whether they have a valid response or an exception and return them as a list to the caller. I would like the caller to be able to expect a list of SomeResponse and be able to interpret them using polymorphism.
However, I'm stuck on determining if this can be done using polymorphism at all, given that the fields for the success and error response are completely different. I have added some pseudo code below on one alternative I have thought of. Basically have SomeResponse be an interface with an isSuccess method. This will allow the caller to know if it's an error or not. However, the caller would still have to cast it to the correct implementation in order to get the value or the error. Is there a better way to approach this? My requirement is being able to return both a success and error response for each given request in the list. If there is an exception, we don't want to abort the entire operation.
public MyProcessorClass {
private final SomeOtherClass someOtherClass;
public List<SomeResponse> process(List<String> requestList) {
return requestList.stream().map(this::procesRequest)
.collectors(Collect.tolist()):
}
private processRequest(String request) {
CompletableFuture completableFuture = CompletableFuture
.supplyAsync(() => {
return new SomeSuccessResponse(someOtherClass.execute(request));
})
.exceptionally(e -> {
return new SomeErrorResponse(e.getCause);
});
return completableFuture.get();
}
}
public interface SomeResponse {
boolean isSuccess();
}
public class SomeSuccessResponse implements SomeResponse {
private final String value;
#Getter
private final boolean success;
public SomeSuccessResponse(String value) {
this.value = value;
this.success = true;
}
}
public class SomeErrorResponse implements SomeResponse {
private final Throwable error;
#Getter
private final boolean success;
public SomeErrorResponse(Throwable error) {
this.error = error;
this.success = false;
}
}
What you want is the visitor pattern https://en.wikipedia.org/wiki/Visitor_pattern
public class Main {
interface IResponse {
void acceptHandler(IResponseHandler handler);
}
static class ResponseA implements IResponse {
#Override
public void acceptHandler(IResponseHandler handler) {
handler.handle(this);
}
}
static class ResponseB implements IResponse {
#Override
public void acceptHandler(IResponseHandler handler) {
handler.handle(this);
}
}
public interface IResponseHandler {
void handle(ResponseA response);
void handle(ResponseB responseB);
}
public static void main(String[] args) {
final IResponseHandler handler = new IResponseHandler() {
#Override
public void handle(ResponseA response) {
System.out.println("Handle ResponseA");
}
#Override
public void handle(ResponseB responseB) {
System.out.println("Handle ResponseB");
}
};
final IResponse someResponse = new ResponseA();
someResponse.acceptHandler(handler);
}
}

How to pass API exception output to through own REST service?

Summary :
I want to pass valid exception output given by one REST service end point to the end user by using my own Rest service.
What I did is, I have called that service in service class using RestTemplate class, it's giving valid output on valid post request. But when I am passing invalid input to it I am getting only '400 BAD REQUEST' result in my service class where I have called that API. But when I am calling that API separately using postman, there I'm getting expected output.
Code sample :
class Abc {
ResponseEntity<String> = response;
static final String url = "https://abc-xyz.com/client-rest-end-point-url";
public ResponseEntity getDetails(RequestInput requestInput) {
try{
response=restTemplate.postForObject(url,requestInput,String.class);
} catch(Exception e) {
ResponseEntity response = (ResponseEntity<ErrorModel>)restTemplate.postForEntity(url,requestInput,ErrorModel.class);
}//try-catch
}//getDetails method
}//class
You can create a custom exception class for your entire application and you can send data in JSON by using throw keyword
Suppose you have exception class is:
public class TestException extends Exception {
private static final long serialVersionUID = 1L;
private String code;
private String detailMessage;
public TestException() {
};
public TestException(String message, String code, String detailMessage) {
super(message);
this.code = code;
this.detailMessage = detailMessage;
}
public TestException(String message, String code) {
super(message);
this.code = code;
}
//TestExceptionResponseCode is another class for message data, if required.
public TestException(TestExceptionResponseCode testExceptionResponseCode) {
super(testExceptionResponseCode.getMessage());
this.code = testExceptionResponseCode.getCode();
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getDetailMessage() {
return detailMessage;
}
public void setDetailMessage(String detailMessage) {
this.detailMessage = detailMessage;
}
}
Now in your case throwing exception can be like :
class Abc {
ResponseEntity<String> = response;
static final String url = "https://abc-xyz.com/client-rest-end-point-url";
public ResponseEntity getDetails(RequestInput requestInput) {
if(requestInput==null){
throw new TestException("FAILED", "1", "Data can't be null");
}
}
Annotate your method with #ExceptionHandler annotation. You can code in seperate class from controller.
#ControllerAdvice
public class YourExceptionHandler {
#ExceptionHandler(CustomException.class)
public String xException() {
return "error/exception";
}
}

Spring #RequestBody Override

I have a Spring controller that takes posts and it works. The only problem is that our SMS providers will be sending us headers that contain keys with a capitalized first letter, for example:
{
"FromPhoneNumber":"15177754077",
"ToPhoneNumber":"17572046106",
"ResponseReceiveDate":"7/29/2014 5:25:10 AM",
"Message":"PIN 1234"
}
Spring will throw an error like:
Could not read JSON: Unrecognized field "FromPhoneNumber" (class com.talksoft.spring.rest.domain.CDynePost), not marked as ignorable (4 known properties: "responseReceiveDate", "toPhoneNumber", "fromPhoneNumber", "message"])
So, there must be a way for me to override this behavior. Here is the controller method that handles the CDyne posts:
#RequestMapping(method = RequestMethod.POST, value="/celltrust")
public ResponseEntity<String> cellTrustPost(#RequestBody CDynePost cDynePost) {
String message = "FAILED";
UserInteraction userInteraction = getUserInteraction(cDynePost);
boolean success = someSpringService.logMessage(userInteraction);
if (success) {
message = "OK";
return new ResponseEntity<String>(message, HttpStatus.ACCEPTED);
} else {
return new ResponseEntity<String>(message, HttpStatus.FAILED_DEPENDENCY);
}
}
and here is the CDynePost class:
public class CDynePost {
private String FromPhoneNumber;
private String ToPhoneNumber;
private String ResponseReceiveDate;
private String Message;
public String getFromPhoneNumber() {
return FromPhoneNumber;
}
public void setFromPhoneNumber(String FromPhoneNumber) {
this.FromPhoneNumber = FromPhoneNumber;
}
public String getToPhoneNumber() {
return ToPhoneNumber;
}
public void setToPhoneNumber(String ToPhoneNumber) {
this.ToPhoneNumber = ToPhoneNumber;
}
public String getResponseReceiveDate() {
return ResponseReceiveDate;
}
public void setResponseReceiveDate(String ResponseReceiveDate) {
this.ResponseReceiveDate = ResponseReceiveDate;
}
public String getMessage() {
return Message;
}
public void setMessage(String Message) {
this.Message = Message;
}
}
I've looked at ObjectMapper but I am not sure how to work this into my controller, and truth be told I'd prefer not to have to write a bunch of extra classes if Spring will do it for free.
Simply annotate your field, getter, or setter with #JsonProperty, specifying the exact name that will appear in the JSON. For example
#JsonProperty("FromPhoneNumber")
private String FromPhoneNumber;

Serialize custom exception to JSON, not all fields are serialized

I'm trying to serialize a custom Exception in Java using writeValueAsString() method from Jackson library. I intend to send it by HTTP to another machine. This is working partialy because not all fields are included in JSON after serialize. The top level exception Throwable implements Serializable interface, and also has some constructors that add info about what is to be serialized. I suppose the truth is somewhere here. Please help with some advices. Here is my custom exception code:
import java.io.Serializable;
public class MyException extends RuntimeException{
private static String type = null;
private static String severity = null;
// somewhere on google I red that should use setters to make serialize work
public static void setType(String type) {
MyException.type = type;
}
public static void setSeverity(String severity) {
MyException.severity = severity;
}
public MyException(String message) {
super(message);
}
}
somewhere in code I use:
MyException exc = new MyException("Here goes my exception.");
MyException.setType(exc.getClass().getSimpleName());
MyException.setSeverity("Major");
throw exc;
and in other place I have:
ObjectMapper mapper = new ObjectMapper();
try {
responseBuilder.entity(mapper.writeValueAsString(MyException) );
}
catch (JsonGenerationException e) {e.printStackTrace(); }
catch (JsonMappingException e) {e.printStackTrace(); }
catch (IOException e) { e.printStackTrace(); }
The result JSON object is:
{"cause":null,"message":"Here goes my exception.","localizedMessage":"Here goes my exception.","stackTrace":[{...a usual stack trace...}]}
Here I also expect to see my type and severity fields.
I made type and severity non-static, and it seems to be working fine. I used the following code, and I see both type and severity in the serialized output.
public class MyException extends RuntimeException
{
private String type = null;
private String severity = null;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getSeverity() {
return severity;
}
public void setSeverity(String severity) {
this.severity = severity;
}
public MyException(String message) {
super(message);
}
}
... and
MyException exc = new MyException("Here goes my exception.");
exc.setType(exc.getClass().getSimpleName());
exc.setSeverity("Major");
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(exc));
Hope this helps!

JAX/Jersey Custom error code in Response

In Jersey, how can we 'replace' the status string associated with a known status code?
e.g.
return Response.status(401).build();
generates a HTTP response that contains:
HTTP/1.1 401 Unauthorized
I (not me, but the client application) would like to see the response as:
HTTP/1.1 401 Authorization Required
I tried the following approaches but in vain:
1) This just adds the String in the body of the HTTP response
return Response.status(401).entity("Authorization Required").build();
2) Same result with this too:
ResponseBuilder rb = Response.status(401);
rb = rb.tag("Authorization Required");
return rb.build();
Appreciate your help!
-spd
To do this in Jersey you have the concept of WebApplicationException class. One method is to simply extend this class and all one of the methods to set the error text that is returned. In your case this would be:
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.core.Response.*;
public class UnauthorizedException extends WebApplicationException {
/**
* Create a HTTP 401 (Unauthorized) exception.
*/
public UnauthorizedException() {
super(Response.status(Status.UNAUTHORIZED).build());
}
/**
* Create a HTTP 404 (Not Found) exception.
* #param message the String that is the entity of the 404 response.
*/
public UnauthorizedException(String message) {
super(Response.status(Status.UNAUTHORIZED).entity(message).type("text/plain").build());
}
}
Now in your code that implements the rest service you would simply throw a new exception of this type, passing in the text value in the constructor e.g.
throw new UnauthorizedException("Authorization Required");
That can create a class like this for each of your web exceptions and throw in a similar fashion.
This is also explained in the Jersey user guide - although the code is actually slightly incorrect:
https://jersey.github.io/nonav/documentation/latest/user-guide.html/#d4e435
I'm not sure JSR 339: JAX-RS 2.0: The Java API for RESTful Web Services already covered this or not.
You might have to extend the Response.StatusType for this.
public abstract class AbstractStatusType implements StatusType {
public AbstractStatusType(final Family family, final int statusCode,
final String reasonPhrase) {
super();
this.family = family;
this.statusCode = statusCode;
this.reasonPhrase = reasonPhrase;
}
protected AbstractStatusType(final Status status,
final String reasonPhrase) {
this(status.getFamily(), status.getStatusCode(), reasonPhrase);
}
#Override
public Family getFamily() { return family; }
#Override
public String getReasonPhrase() { return reasonPhrase; }
#Override
public int getStatusCode() { return statusCode; }
public ResponseBuilder responseBuilder() { return Response.status(this); }
public Response build() { return responseBuilder().build(); }
public WebApplicationException except() {
return new WebApplicationException(build());
}
private final Family family;
private final int statusCode;
private final String reasonPhrase;
}
And here are some extended statust types.
public class BadRequest400 extends AbstractStatusType {
public BadRequest400(final String reasonPhrase) {
super(Status.BAD_REQUEST, reasonPhrase);
}
}
public class NotFound404 extends AbstractStatusType {
public NotFound404(final String reasonPhrase) {
super(Status.NOT_FOUND, reasonPhrase);
}
}
This is how I do.
#POST
public Response create(final MyEntity entity) {
throw new BadRequest400("bad ass").except();
}
#GET
public MyEntity read(#QueryParam("id") final long id) {
throw new NotFound404("ass ignorant").except();
}
// Disclaimer
// I'm not a native English speaker.
// I don't know what 'bad ass' or 'ass ignorant' means.

Categories