38

I am new to this, trying to achieve reading some docs but its not working, please bear with me.

I have created a UserNotFoundMapper using ExceptionMappers like this:

public class UserNotFoundMapper implements ExceptionMapper<UserNotFoundException> {

@Override
public Response toResponse(UserNotFoundException ex) {
    return Response.status(404).entity(ex.getMessage()).type("text/plain").build();
}

}

This in my service:

@GET
@Path("/user")
public Response getUser(@QueryParam("id") String id) throws UserNotFoundException{
    //Some user validation code with DB hit, if not found then
    throw new UserNotFoundException();
}

The UserNotFoundException is an User-Defined Exception.

I tried this:

public class UserNotFoundException extends Exception {
       //SOME block of code 
}

But when I invoke the service, the UserDefinedExceptionMapper is not getting invoked. It seems I might be missing something in the UserDefinedException. How to define this exception then?

Please let me know how to define the UserNotFoundException.

WhoAmI
  • 819
  • 4
  • 18
  • 35

3 Answers3

55

You need to annotate your exception mapper with @Provider, otherwise it will never get registered with the JAX-RS runtime.

@Provider
public class UserNotFoundMapper implements
        ExceptionMapper<UserNotFoundException> {
    @Override
    public Response toResponse(UserNotFoundException ex) {
        return Response.status(404).entity(ex.getMessage()).type("text/plain")
                .build();
    }
}
Perception
  • 79,279
  • 19
  • 185
  • 195
  • Thanks for the reply. I need to know a few things related to this: Where lies the difference between using a WebApplicationException and using an ExceptionMapper? I am not sure under which circumstances which should be used :( Can you please provide your inputs on this? – WhoAmI Mar 03 '13 at 13:00
  • 3
    @WhoAmI - web application exceptions are automatically mapped by the JAX-RS provider. So, as opposed to using exception mappers, for example, you could extend `WebApplicationException` and override it's `getResponse` method. Some people prefer to do this for all exceptions that they define solely for their JAX-RS implementation. Mappers are great though, for mapping exceptions that do not extend from WebApplicationException (e.g all the standard Java exceptions, exceptions from third party libraries etc etc). – Perception Mar 03 '13 at 13:08
  • This statement: `web application exceptions are automatically mapped by the JAX-RS provider` I have seen it in a lot of docs I am reading around over the net. But not able to understand what actually it means in simple terms and what is the importance/usefulness of it :( Can you please explain? – WhoAmI Mar 03 '13 at 13:13
  • 1
    @WhoAmI: The [Jersey User Guide](http://jersey.java.net/nonav/documentation/latest/user-guide.html#d4e435) lays it out pretty clearly, if not in great detail. It means that a `WebApplicationException` holds a `Response` inside it, and when you throw one of them, that `Response` is what gets returned to the user. – Ryan Stewart Mar 03 '13 at 13:25
  • 2
    @WhoAmI: Which exception approach to use is up to you. The strength of the `ExceptionMapper` is that your code can remain independent of Jersey, and you can also map exceptions that are beyond your control (framework exceptions). The strength of the `WebApplicationException` is that it's simpler to use. – Ryan Stewart Mar 03 '13 at 13:26
  • Ok so rather than returning a `Response` like we do in ExceptionMappers; we are supposed to use a super(Response.....) for the `WebApplicationException`. Thats all right? – WhoAmI Mar 03 '13 at 13:29
  • 1
    @ryanstewart - actually the mapping behavior for WebApplicationException is part of the JAX-RS specification, so it's portable across providers. Which is a great thing. – Perception Mar 03 '13 at 16:30
  • @Perception: Good call. I often forget to make that distinction. `s/Jersey/JAX-RS/` in my last comment! – Ryan Stewart Mar 03 '13 at 22:05
3

What I usually do when creating APIs is create my own exception that extends from RuntimeException so I don't necessarily have to catch my exception.

Here's an example:

NOTE: I'm using JAX-RS with Jersey

First: create my own Exception that extends from RuntimeException.

public class ExceptionName extends RuntimeException {

private int code;
private String message;

public int getCode(){
    return code;
}

public String getMessage(){
    return message;
}

public ExceptionName(int code, String message) {
    this.code = code;
    this.message = message;
}

}

Also implement a ExceptionMapper

@Provider
public class ExceptionName implements ExceptionMapper<ExceptionName>{

    @Override
    public Response toResponse(ExceptionName exception) {
        return Response.status(exception.getCode()).entity(exception.getMessage()).build();
    }

}

And every time that I want to throw an exception I just do it like this anywhere, the exception mapper will take care of returning a response to the client consuming the API

throw new ExceptionName(500,"there was an error with something here");
Bugs
  • 4,491
  • 9
  • 32
  • 41
-1

One small remark , try to Use Response.Status.NOT_FOUND rather than using 404 etc. Code will be more readable and less prone to typos , the same goes for "text/plain". Below is the code that will handle exception as you mentioned. Oh and one more thing remember to annotate your method @Produces(MediaType.TEXT_PLAIN) in your interface


    public class UserNotFoundException extends Exception {
        //...
    }

    public class UserServiceImpl implements UserService {

        @Override 
        public Response getUser(@QueryParam("id") String id) {
            final Response response;
            try{
                // call user method
                //if everything is ok
                response = Response.status(Response.Status.OK).entity(whateverYouWant).type(MediaType.TEXT_PLAIN).build();
            } catch(UserNotFoundException ex) {         
                response = new UserNotFoundMapper().toResponse(ex);
            }

            return response;
        }
    }

    In client slide you can check 

    public static boolean isUserExists(final Response serverResp) {
        return serverResp != null && serverResp.getStatus() == Response.Status.NOT_FOUND.getStatusCode();
    }

sol4me
  • 15,233
  • 5
  • 34
  • 34
  • 6
    You don't invoke the mapper manually, and you wouldn't use Jersey-specific classes in your service layer. That's the whole point of mappers. – Ryan Stewart Mar 03 '13 at 12:28
  • @RyanStewart Thanks for the inputs. I am new to this and trying to learn it. In teh above postedd code; if I remove the mapper invocation line with `throw new UserNotFoundException();` then it is fine right? I cant understand what you meant by `you wouldn't use Jersey-specific classes in your service layer`. Can you please explain it in a bit details? – WhoAmI Mar 03 '13 at 13:06
  • @WhoAmI The class you are in is called UserServiceImpl and is part of the service layer, not the controller layer. You don't want to force a particular implementation like jax-rs into the rest of your code. It's usage should be terminated at the controller layer. ExceptionMapper give you a way to handle exceptions via jersey without adding `Response.status`... throughout your code. Service layer code should only do business logic. – gaoagong Apr 20 '20 at 20:38