16

The task: Instead of receiving general HTTP 500 Internal Server Error in my stacktrace and the same horrible stacktrace on the client side I want to see my customized message with another statuscode (403 for example), that it will be much clearer for the developer, what has happend. And add some message to User about the Exception.

Here are couple of changed classes from my application:

SERVER PART:

AppException.class - all my Server Response exceptions (before giving back to client) I want to transform into this exception. Kinda standard entity class

public class AppException extends WebApplicationException {

Integer status;

/** application specific error code */
int code;

/** link documenting the exception */
String link;

/** detailed error description for developers */
String developerMessage;

public AppException(int status, int code, String message, String developerMessage, String link) {
    super(message);
    this.status = status;
    this.code = code;
    this.developerMessage = developerMessage;
    this.link = link;
}

public int getStatus() {
    return status;
}

public void setStatus(int status) {
    this.status = status;
}

public int getCode() {
    return code;
}

public void setCode(int code) {
    this.code = code;
}

public String getDeveloperMessage() {
    return developerMessage;
}

public void setDeveloperMessage(String developerMessage) {
    this.developerMessage = developerMessage;
}

public String getLink() {
    return link;
}

public void setLink(String link) {
    this.link = link;
}

public AppException() {
}

public AppException(String message) {
    super("Something went wrong on the server");
}
}

ÀppExceptionMapper.class - mapps my AppException to the JAX-RS Runtime, instead standard exception, client receives AppException.

    @Provider
public class AppExceptionMapper implements ExceptionMapper<AppException> {

    @Override
    public Response toResponse(AppException exception) {
        return Response.status(403)
                .entity("toResponse entity").type("text/plain").build();
    }


}

ApplicationService.class- my Service class that throws AppException

 @Path("/applications")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public interface ApplicationService {


    @DELETE
    @Path("/deleteById")
    void deleteById(@NotNull Long id) throws AppException;
}

CLIENT PART:

ErrorHandlingFilter.class- my Response catcher of the AppException. Here I want to transform each Response exception to another exception depending on the status.

@Provider
public class ErrorHandlingFilter implements ClientResponseFilter {

    private static ObjectMapper _MAPPER = new ObjectMapper();

    @Override
    public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
        if (responseContext.getStatus() != Response.Status.OK.getStatusCode()) {
            if(responseContext.hasEntity()) {
                Error error = _MAPPER.readValue(responseContext.getEntityStream(), Error.class);
                String message = error.getMessage();

                Response.Status status = Response.Status.fromStatusCode(responseContext.getStatus());
                AppException clientException;

                switch (status) {

                case INTERNAL_SERVER_ERROR:
                    clientException = new PermissionException(message);
                    break;


                case NOT_FOUND:
                    clientException = new MyNotFoundException(message);
                    break;

                default:
                    clientException =  new WhatEverException(message);
                }
                    throw clientException;
        }
    }
    }
}

PermissionException.class - exception in what I want to transform AppException, if it came with 500 status code.

public class PermissionException extends AppException{

        public PermissionException(String message) {
    super("403 - Forbidden. You dont have enough rights to delete this Application");

}

Integer status;

/** application specific error code */
int code;

/** link documenting the exception */
String link;

/** detailed error description for developers */
String developerMessage;

public PermissionException(int status, int code, String message, String developerMessage, String link) {
    super(message);
    this.status = status;
    this.code = code;
    this.developerMessage = developerMessage;
    this.link = link;
}

public int getStatus() {
    return status;
}

public void setStatus(int status) {
    this.status = status;
}

public int getCode() {
    return code;
}

public void setCode(int code) {
    this.code = code;
}

public String getDeveloperMessage() {
    return developerMessage;
}

public void setDeveloperMessage(String developerMessage) {
    this.developerMessage = developerMessage;
}

public String getLink() {
    return link;
}

public void setLink(String link) {
    this.link = link;
}

public PermissionException() {}


}

ApplicationPresenter.class- piece of UI logic, where I want something to do with PermissionException thrown by the ErrorHandlingFilter.

@SpringPresenter
public class ApplicationPresenter implements ApplicationView.Observer {

@Resource
    private ApplicationService applicationService;

    @Resource
    private UiEnvironment uiEnvironment;

@Override
    public void deleteSelectedApplication(BeanItemGrid<Application> applicationGrid) {

        try {
applicationService.deleteById(applicationGrid.getSelectedItem().getId());
                    } catch (PermissionException e) {
                        e.printStackTrace();
                        e.getMessage();
                    } catch (AppException e2) {
                    }
}
}

How can I resolve my problem? I am still receiving standard 500 InternalErrorException.

UPDATED ALMOST THE WHOLE QUESTION ONE MORE TIME!

22
  • When you have an ExceptionMapper, you don't catch the exception yourself, but have the framework catch it, when the resource method is invoked on an HTTP request. (I don't really understand what your last class does; is it client code?) Commented Aug 16, 2016 at 9:57
  • @gsl yeah, I just wanted to show where I catch my Exception. I have tried to do the same but without PermissionExceptionMapper. class, just only PermissionException. and it didn't work( Commented Aug 16, 2016 at 10:00
  • Well, you just don't have to catch it in your own code. (Apart from a test program, but I don't see how this makes sense.) Commented Aug 16, 2016 at 10:20
  • @gsl if you write your first comment as an answer to the post, I will accept it as the answer. It seems that I have fully wrong understood the concept of ExctentionMapper Commented Aug 16, 2016 at 10:52
  • Why don't you handle Response received from the ApplicationService / PermissionExceptionMapper? Commented Aug 18, 2016 at 14:20

5 Answers 5

5

The proper way to perform error handling is by registering ExceptionMapper instances that know what response should be returned in case of specific (or generic) exception.

@Provider
public class PermissionExceptionHandler implements ExceptionMapper<PermissionException>{
    @Override
    public Response toResponse(PermissionException ex){
        //You can place whatever logic you need here
        return Response.status(403).entity(yourMessage).build();
    }  
}

Please take a look at my other answer for more details: https://stackoverflow.com/a/23858695/2588800

Sign up to request clarification or add additional context in comments.

9 Comments

ty for your reply. PermissionException I create by myself of the client side. I have for this my ErrorHandlingFilter. should I have on client Mapper for PermissionExceptionas well?
All the exception mappers reside on the server side. When the REST endpoint on the server throws an exception, it will be intercepted by the exception mapper which will generate a proper response that will be sent to the client.
thanks. I am using your suggested variant in my ÀppExceptionMapper` .But my Mapperdon't want to throw the propper response to my client((
Can you show me how does your mapper looks like, a gist or pastebin maybe ? Also have you checked if the mapper is called at all ?
sure - (pastebin.com/mwbrHg99). he is never benn called, trying to understand why?.
|
4

When you have an ExceptionMapper, you don't catch the exception yourself, but have the framework catch it, when the resource method is invoked on an HTTP request.

Comments

2

This is a Jersey example, but you can extract the needed informations from here. I would only throw an exception and map this exception to any wanted response in the end.

Lets assume you have following ressource method, thowing the exception:

@Path("items/{itemid}/")
public Item getItem(@PathParam("itemid") String itemid) {
  Item i = getItems().get(itemid);
  if (i == null) {
    throw new CustomNotFoundException("Item, " + itemid + ", is not found");
  }

  return i;
}

Create your exception class:

public class CustomNotFoundException extends WebApplicationException {

  /**
  * Create a HTTP 404 (Not Found) exception.
  */
  public CustomNotFoundException() {
    super(Responses.notFound().build());
  }

  /**
  * Create a HTTP 404 (Not Found) exception.
  * @param message the String that is the entity of the 404 response.
  */
  public CustomNotFoundException(String message) {
    super(Response.status(Responses.NOT_FOUND).
    entity(message).type("text/plain").build());
  }
}

Now add your exception mapper:

@Provider
public class EntityNotFoundMapper implements ExceptionMapper<CustomNotFoundException> {
  public Response toResponse(CustomNotFoundException  ex) {
    return Response.status(404).
      entity("Ouchhh, this item leads to following error:" + ex.getMessage()).
      type("text/plain").
      build();
  }
}

In the end you have to register your exception mapper, so it can be used in your application. Here is some pseudo-code:

register(new EntityNotFoundMapper());
//or
register(EntityNotFoundMapper.class);

2 Comments

What do you mean by register ? And how to register ?
For example in Dropwizard you can register your exception mapper.
1

I have a different approach here. You can try this when starting your jetty server in the main java method

public static void main(String[] args) throws UnknownHostException, JSONException, IOException, Exception {

        MyMain myMain = new MyMain();

        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
        context.setContextPath("/");

        Server jettyServer = new Server(5550);
        jettyServer.setHandler(context);
        context.setErrorHandler(new ErrorHandler());
        // default error handler for resources out of "context" scope
        jettyServer.addBean(new ErrorHandler());

        ServletHolder jerseyServlet = context.addServlet(org.glassfish.jersey.servlet.ServletContainer.class, "/*");
        jerseyServlet.setInitOrder(0);

        // Tells the Jersey Servlet which REST service/class to load.
        jerseyServlet.setInitParameter("jersey.config.server.provider.classnames",
                ControllerInn.class.getCanonicalName() );

        try {
            jettyServer.start();            
            jettyServer.join();

        } catch (Exception ex) {
            Logger.getLogger(ControllerInn.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            jettyServer.destroy();
        }
    }
    /**
     * Dummy error handler that disables any error pages or jetty related messages and returns our
     * ERROR status JSON with plain HTTP status instead. All original error messages (from our code) are preserved
     * as they are not handled by this code.
     */
    static class ErrorHandler extends ErrorPageErrorHandler {
        @Override
        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {
            response.getWriter()
            .append("{\"message\":\"HTTP ERROR ")
            .append(String.valueOf(response.getStatus()))
            .append("\"}");
        }
    }

So you can get an output like this

{"message":"HTTP ERROR 500"}

You can Reference from here

Comments

0

Suggested correctly above, ideal practice is to let the framework catch the Exception for you now that you have implemented an ExceptionMapper. However, one important point overviewing the phenomena which you are executing: if you need to handle any uncaught exceptions, you need to have an Exception class implementing ExceptionMapper which maps to Throwable

public class UncaughtExcep implements ExceptionMapper<Throwable>{

   @Override 
   public Response toResponse(Throwable e){

    }
}

Assuming your class WhatEverException caters to that. If not, then its a good practice to implement

2 Comments

thank you for your reply. But.. I have tried everything, I have tried to set Throwable, Exception, WebApplicationException. None worked for me. The point is that my ExceptionMapper.classis never been called. JAX-RS Runtime is just ignoring my ExceptionMapper.class with @Provider.
For mapping exceptions based on WebApplicationException, you can once try an alternate approach of removing ExceptionMapper and instead make the exception extend to WebApplicationException and override the getResponse method. For exceptions that do not extend from WebApplicationException can use the usual approach of ExceptionMapper

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.