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!

t_sologub
  • 855
  • 2
  • 15
  • 26
  • 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?) – gsl Aug 16 '16 at 09: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( – t_sologub Aug 16 '16 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.) – gsl Aug 16 '16 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 – t_sologub Aug 16 '16 at 10:52
  • Why don't you handle Response received from the ApplicationService / PermissionExceptionMapper? – Justinas Jakavonis Aug 18 '16 at 14:20
  • @Justas mmm. Can you give/show an example? ty. – t_sologub Aug 18 '16 at 14:22
  • I don't understand the problem and architecture completely you're trying to solve. If you are you using ApplicationService as injected service than you should consider disabling ExceptionMapper and use try/catch for PermissionException in your presentation layer. If you want to use ApplicationService as REST service client then you should get Response for every call and validate its HTTP status. – Justinas Jakavonis Aug 18 '16 at 14:34
  • @Justas - " If you want to use ApplicationService as REST service client then you should get Response for every call and validate its HTTP status." - you mean like here (last code example in the question post) http://stackoverflow.com/questions/22561527/handling-custom-error-response-in-jax-rs-2-0-client-library – t_sologub Aug 18 '16 at 14:44
  • Something like the accepted answer in your mentioned thread. But why do you need this? Why don't you catch PermissionException in ApplicationPresenter and take appropriate action? – Justinas Jakavonis Aug 18 '16 at 15:45
  • Yeah, I don't get why the REST resource method is called in the client. Where's the server?? There is no HTTP transaction involved!??! – gsl Aug 18 '16 at 16:56
  • @gsl can you have a look one more time? I have tried as you suggested. Question is updated. thanks – t_sologub Aug 19 '16 at 10:13
  • You write "mapps my AppException to the JAX-RS Runtime, instead standard exception, client receives AppException" - that makes clear your misconception: The client won't receive an execption. – gsl Aug 19 '16 at 12:37
  • @gsl but he would otherwise return 403, as in `toResponse` in `AppExceptionMapper`. and not 500. with that phrase I meant, that I want to catch on the client the exception thrown by the server (`AppException`) – t_sologub Aug 19 '16 at 12:41
  • I still don't understand what you do on the client side: How can you call `applicationService.deleteById()` when that's a method on the server. – gsl Aug 19 '16 at 13:17
  • @gsl that is called from my `Presenter`, he has some UI logic He talks to `View` and he can talk to my `ApplicationService` – t_sologub Aug 19 '16 at 13:26
  • @gsl forgot to mention, `ApplicationService`its my API. I talk to my API from Presenter `ApplicationPresenter`. And this API has Implementation – t_sologub Aug 19 '16 at 13:35
  • But the JAX-RS Server classes should not reside in your client application, but on some server which you contact via HTTP!?!? – gsl Aug 19 '16 at 14:05
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/121357/discussion-between-tyler-and-gsl). – t_sologub Aug 19 '16 at 14:14
  • Would like to, but I cannot connect to chat rooms from where I am online now. Sorry. – gsl Aug 19 '16 at 14:28
  • @gsl okey, not a problem. i connect to my Service API from my client via HTTP. Sry if my explanation is not so clear to u – t_sologub Aug 19 '16 at 14:30
  • Hi @t_sologub! Could you find a solution for your problem. As far i understand this, you're trying to map the error response with code 4xx or 5xx to a custom exception on client side? I'm facing the same issue, but think that this won't work, since jax-rs throws a particular subtype of WebApplicationException in case of http-coce >=400 at client side. – My-Name-Is Dec 02 '22 at 16:06
  • Hey @My-Name-Is. Unfortunately, almost six years have passed. I can't remember anything from that time or how I resolved the issue. Sorry. – t_sologub Dec 03 '22 at 04:47

5 Answers5

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.

gsl
  • 676
  • 5
  • 16
4

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

Community
  • 1
  • 1
Svetlin Zarev
  • 14,713
  • 4
  • 53
  • 82
  • 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 `PermissionException`as well? – t_sologub Aug 19 '16 at 12:14
  • 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. – Svetlin Zarev Aug 19 '16 at 12:20
  • thanks. I am using your suggested variant in my ÀppExceptionMapper` .But my `Mapper`don't want to throw the propper response to my client(( – t_sologub Aug 19 '16 at 12:24
  • 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 ? – Svetlin Zarev Aug 19 '16 at 12:31
  • sure - (http://pastebin.com/mwbrHg99). he is never benn called, trying to understand why?. – t_sologub Aug 19 '16 at 12:37
  • I guess your server is not scanning for jax-rs resources. You can try to register it 'manually': http://blog.dejavu.sk/2013/11/19/registering-resources-and-providers-in-jersey-2/ – Svetlin Zarev Aug 19 '16 at 12:43
  • Yeah, better double-check the mapper. The annotation must be `javax.ws.rs.ext.Provider`. Is the jar scanning active? Or are you using manuell Registration in an Application class like this: /** @see javax.ws.rs.core.Application#getClasses() */ @Override public Set> getClasses() { Set> classes = ... classes.add( MyResource.class ); classes.add( MyExceptionMapper.class ); ` – gsl Aug 19 '16 at 12:44
  • yeap. the annotation is correct, I haven't set it manually. @SvetlinZarev I'll try manually. ty. but I dont think that will help – t_sologub Aug 19 '16 at 12:46
  • @SvetlinZarev emmmm. I checked your link, but mmmm, I can't find `ResourceConfig.class` like on the example – t_sologub Aug 19 '16 at 13:08
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);
hiaclibe
  • 636
  • 9
  • 25
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

Young Emil
  • 2,220
  • 2
  • 26
  • 37
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

Akash Mishra
  • 682
  • 1
  • 5
  • 13
  • 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.class`is never been called. JAX-RS Runtime is just ignoring my `ExceptionMapper.class` with `@Provider`. – t_sologub Aug 25 '16 at 10:36
  • 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 – Akash Mishra Aug 26 '16 at 21:04