JAX-RS and EJB exception handling - java

I'm having trouble handling exceptions in my RESTful service:
#Path("/blah")
#Stateless
public class BlahResource {
#EJB BlahService blahService;
#GET
public Response getBlah() {
try {
Blah blah = blahService.getBlah();
SomeUtil.doSomething();
return blah;
} catch (Exception e) {
throw new RestException(e.getMessage(), "unknown reason", Response.Status.INTERNAL_SERVER_ERROR);
}
}
}
RestException is a mapped exception:
public class RestException extends RuntimeException {
private static final long serialVersionUID = 1L;
private String reason;
private Status status;
public RestException(String message, String reason, Status status) {
super(message);
this.reason = reason;
this.status = status;
}
}
And here is the exception mapper for RestException:
#Provider
public class RestExceptionMapper implements ExceptionMapper<RestException> {
public Response toResponse(RestException e) {
return Response.status(e.getStatus())
.entity(getExceptionString(e.getMessage(), e.getReason()))
.type("application/json")
.build();
}
public String getExceptionString(String message, String reason) {
JSONObject json = new JSONObject();
try {
json.put("error", message);
json.put("reason", reason);
} catch (JSONException je) {}
return json.toString();
}
}
Now, it is important for me to provide both a response code AND some response text to the end user. However, when a RestException is thrown, this causes an EJBException (with message "EJB threw an unexpected (non-declared) exception...") to be thrown as well, and the servlet only returns the response code to the client (and not the response text that I set in RestException).
This works flawlessly when my RESTful resource isn't an EJB... any ideas? I've been working on this for hours and I'm all out of ideas.
Thanks!

The problem seems to be connected with EJB Exception Handling. By the specification any system exception (that is - any RuntimeException not specifically marked as Application Exception) that is thrown from within a managed bean will be packaged into an EJBException and later, if needed, into RemoteException thrown to the client. That is a situation you seem to be in, and in order to avoid that you can either:
change your RestException into a checked exception and handle it as such
use #ApplicationException annotation on your RestException
create EJBExceptionMapper and extract information needed from (RestfulException) e.getCause()

A similar case works for me when RestException extends javax.ws.rs.WebApplicationException

Related

How can I get a custom message from WebApplicationException?

I have a service A that makes post request to another controller (B). This is my service making post request. Controller B is not in the same project as service A, so B throws Bad request (400) and service A turns 400 request to WebApplicationException:
WebClient client = tokenAuth.addAuthentication(WebClient.create(url))
.type(AccelaradMediaType.SMR_IMAGE_SHARE_V3_JSON)
.accept(AccelaradMediaType.SMR_SHARE_RESULT_JSON);
String response = client.post(body, String.class);
catch (WebApplicationException e) {
//get message from exception and print
}
And this is other controller(B) that my service is making post request:
#POST
#Path("/shares")
#Consumes({AccelaradMediaType.SMR_IMAGE_SHARE_V3_JSON, AccelaradMediaType.SMR_IMAGE_SHARE_V3_XML})
#Produces({AccelaradMediaType.SMR_SHARE_RESULT_JSON, AccelaradMediaType.SMR_SHARE_RESULT_XML})
public ShareResult shareV3() {
ShareResult result = null;
try {
result = shareStudies();
}
catch (StudyShareException e) {
LOG.error(e.getMessage(), e);
throw new BadRequestException(e.getMessage());
}
return result;
}
public ShareResult shareStudies() {
try {
//some logic
}
catch (InvitationException e) {
String message = "Invitation is pending";
throw new StudyShareException(message, e);
}
}
And here are StudyShareException class and BadRequestException class:
public class StudyShareException extends Exception {
public StudyShareException(String message, Throwable cause) {
super(message, cause);
}
}
public class BadRequestException extends WebApplicationException {
private static final long serialVersionUID = 1L;
public BadRequestException(String message) {
this(message, null);
}
public BadRequestException(String message, Throwable cause) {
super(cause, Response.status(Response.Status.BAD_REQUEST).entity(message).type(MediaType.TEXT_PLAIN).build());
}
}
When service A makes a post request, it does go into the catch block and controller B prints out error in the stack trace with "Invitation is pending" message.
My goal is to print out "Invitation is pending" from service A as well. I tried e.getResponse() or e.getResponse().getEntity() or e.getMessage(), nothing has worked. Is it even possible to get custom message from service A? If so, how can I achieve this?
Why are you catching WebApplicationException in service A when service B is throwing StudyShareException? You need to catch the correct exception in service A
try {
WebClient client = tokenAuth.addAuthentication(WebClient.create(url))
.type(AccelaradMediaType.SMR_IMAGE_SHARE_V3_JSON)
.accept(AccelaradMediaType.SMR_SHARE_RESULT_JSON);
String response = client.post(body, String.class);
catch (StudyShareException e) {
//get message from exception and print
}
Now if you are trying to catch all exceptions extending WebApplicationException then you should make StudyShareException extend WebApplicationException as well.
That being said, perhaps you shouldn't catch any exception at all just let service A just bubble up the exception thrown in service B. However, that's up to you, you may want to throw a different message or a different exception in service A.

Spring Soap Webservice exception handling

I am creating soap web service using spring. I am getting hibernate exception while trying to save the request.
I am trying to catch the hibernate exception in the catch block but control not even coming to the catch block
and soap service returning with the soap fault error message. Below are the list of classes which i am using,
Could any one please let me know how to handle the exception and rethrow the exception.
#WebService(serviceName = "submitService")
public class SubmitService extends AbstractWebServiceImpl {
#Autowired
private Validate validate;
#WebMethod
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public ResponseData submitRequest(RequestData request) {
ResponseData response = validate.submitRequest(request);
return response;
}
}
My Base class
public class AbstractWebServiceImpl extends SpringBeanAutowiringSupport {
#Resource
private WebServiceContext webServiceContext;
protected void handleWSException(Exception e) throws BusinessFault, InfrastructureFault {
if ( e instanceof BusinessException) {
ReturnMessage rm = ((BusinessException)e).getReturnMessage();
throw new BusinessFault(rm, e);
} else if (e instanceof BaseException) {
ReturnMessage rm = ((BaseException)e).getReturnMessage();
throw new InfrastructureFault(rm, e);
} else {
ReturnMessage rm = new ReturnMessage(ReturnCode.GENERIC_WEB_SERVICE_ERROR, e.toString());
throw new InfrastructureFault(rm, e);
}
}
public void setWebServiceContext(WebServiceContext webServiceContext) {
this.webServiceContext = webServiceContext;
}
public WebServiceContext getWebServiceContext() {
return webServiceContext;
}
}
My Business layer implementation class
#Component
public class ValidateImpl implements Validate {
#Autowired
private SomeHibernateDao dao;
#Override
#Transactional
public ResponseData submitRequest(RequestData request) {
ResponseData response = new ResponseData();
try {
dao.save(request);
} catch (Exception e) {
// Control never execute this block of code if dao layer throwing any exception.
// I want to catch the exception here modify the response and return to the client
e.printStackTrace();
response.setErrorDetails("More meaningful error message");
}
return response;
}
This code returning default soap error message back to client.
I want to catch the exception and modify the exception before returning to client. Please let me know what change i have to make so that i can handle the soap error message before i return the response back to client.
I am able to catch the exception in SubmitService itself but not sure why not able to catch the exception in ValidateImpl. however my issue is resolved now.

How to get JAX-WS response HTTP status code

When calling a JAX-WS endpoint, how can I get the HTTP response code?
In the sample code bellow, when calling the web service at port.getCustomer(customerID); an Exception may be thrown, such as 401 or 500.
In such cases, how can I get the HTTP status code from the HTTP response?
#Stateless
public class CustomerWSClient {
#WebServiceRef(wsdlLocation = "/customer.wsdl")
private CustomerService service;
public void getCustomer(Integer customerID) throws Exception {
Customer port = service.getCustomerPort();
port.getCustomer(customerID); // how to get HTTP status
}
}
Completing #Praveen answer, you have to turn the port into a raw BindingProvider and then get the values from the context.
Don't forget that transaction will be marked for rollback if an exception occours in your managed web service client.
#Stateless
public class CustomerWSClient {
#WebServiceRef(wsdlLocation = "/customer.wsdl")
private CustomerService service;
public void getCustomer(Integer customerID) throws Exception {
Customer port = service.getCustomerPort();
try {
port.getCustomer(customerID);
} catch(Exception e) {
throw e;
} finally {
// Get the HTTP code here!
int responseCode = (Integer)((BindingProvider) port).getResponseContext().get(MessageContext.HTTP_RESPONSE_CODE);
}
}
}
The below post is similar to your question. Hopefully it should work for you
https://stackoverflow.com/a/35914837/4896191

Return exception from JAX-RS Rest Service as JSON

Is it in some way possible that an exception thrown from a rest service is returned as JSON? I have a JAX-RS Rest Service where I would like to achieve this. When I throw it now, it's mapped to an HTML response, which is not what i want. From what I have understood an ExceptionMapper will also map it to HTML? Is there any other alternative or libraries that allows the exception to be returned in JSON format?
It will respond as JSON.
#Provider
#Singleton
public class ExceptionMapperProvider implements ExceptionMapper<Exception>
{
#Override
public Response toResponse(final Exception exception)
{
return Response.status(HttpStatusCodes.STATUS_CODE_SERVER_ERROR).entity(new BasicResponse(InternalStatus.UNHANDLED_EXCEPTION, exception.getMessage())).type(MediaType.APPLICATION_JSON).build();
}
}
#XmlRootElement
public class BasicResponse {
public String internalStatus;
public String message;
public BasicResponse() {}
public BasicResponse(String internalStatus, String message){
this.internalStatus = internalStatus;
this.message = message;
}
}
You can create custom exception,It takes JSON request and response
#POST
#Path("/betRequest")
#Consumes({ "application/json", "application/x-www-form-urlencoded" })
#Produces({ "application/json", "application/x-www-form-urlencoded" })
public Response getBetRequest(String betRequestParams, #Context HttpServletRequest request)
{
BetResponseDetails betResponseDetails = new BetResponseDetails();
try{
//you code here
}
catch (JSONException ex)
{
ex.printStackTrace();
betResponseDetails.setResponseCode("9002");//your custom error code
betResponseDetails.setResponseStatus("Bad Request");//custom status
betResponseDetails.setResponseMessage("The request body contained invalid JSON");//custom error massage
return Response.status(200).entity(betResponseDetails).build();
}
}
Create One POJO BetResponseDetails
public class BetResponseDetails {
private String ResponseStatus;
private String ResponseCode;
private String ResponseMessage;
// getter/setter
.......
}
get the response data in a structure with status and data, if the status is error, show the correct message.
you can try this way
{
"status": "error",
"data": {
"message": "information of error message"
}
}
catch your exceptions, then build a response object in a standardized format such as
error: {
code: 'XXX',
status: HTTPStatus,
message: 'my error message'
}
And send it as a response with an error status (from Response.Status, usually 4xx or 5xx)

How to return HTTP Codes in web services

I've a SOAP web service built in Java.
If my method runs into an exception I want to return a "HTTP CODE 500".
Is it possible? If yes how?
(Web service is running on Tomcat 6)
maybe you should simply throw a qualified Exception yourself which then will be sent back to the client as a soap fault.
W3C tells us this:
In case of a SOAP error while processing the request, the SOAP HTTP
server MUST issue an HTTP 500 "Internal Server Error" response and
include a SOAP message in the response containing a SOAP Fault element
(see section 4.4) indicating the SOAP processing error.
http://www.w3.org/TR/2000/NOTE-SOAP-20000508/
Messing with http response codes could be dangerous as some other client might expect a different response. In your case you'd be lucky because you want exactly the the behaviour as specified by W3C. So throw an Exception ;)
How to do that? Take a look here:
How to throw a custom fault on a JAX-WS web service?
Greetings
Bastian
Since the JAX-WS is based on servlets, you can do it. You can try the next:
#WebService
public class Calculator {
#Resource
private WebServiceContext ctx;
public int division (int a, int b) {
try {
return a / b;
} catch (ArithmeticException e) {
sendError(500, "Service unavailable for you.");
return -1; // never send
}
}
private void sendError(int status, String msg) {
try {
MessageContext msgCtx = ctx.getMessageContext();
HttpServletResponse response =
(HttpServletResponse) msgCtx.get(MessageContext.SERVLET_RESPONSE);
response.sendError(status, msg);
} catch (IOException e) {
// Never happens or yes?
}
}
}
However, I prefer to use JAX-RS to do something similar.
#PUT
#Path("test")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response update( //
#FormParam("id") int id,
#FormParam("fname") String fname,
#FormParam("lname") String lname
) {
try {
// do something
return Response.ok("Successfully updated",
MediaType.TEXT_PLAIN_TYPE).build();
} catch (Exception e) {
LOG.error("An error occurred", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("An error occurred")
.type(MediaType.TEXT_PLAIN_TYPE).build();
}
}

Categories