Search code examples
javarestjerseyjax-rsexceptionmapper

Jersey invoking sub resource wrongly


I am learning RESTful web services using JAX-RS. I have a MessageResource and a sub resource CommentResource.

In my MessageResource I mapped comment sub resource with test method, which I commented in the MessageResource class because I don't want CommentResource to be invoked.

package org.kaushik.javabrains.messenger.resource;

import java.net.URI;
import java.util.List;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;

import org.glassfish.jersey.server.Uri;
import org.kaushik.javabrains.messenger.exception.DataNotFoundException;
import org.kaushik.javabrains.messenger.model.Message;
import org.kaushik.javabrains.messenger.services.MessageService;

@Path("/messages")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class MessageResource {

    public MessageResource() {
        // TODO Auto-generated constructor stub
    }

    @GET
    public List<Message> getAllMessage() {

        MessageService obj = new MessageService();
        return obj.getAllMessages();
    }

    @GET
    @Path("/{messageId}")
    public Message getMessageById(@PathParam("messageId") String messageId) {

        MessageService obj = new MessageService();
        Message retrivedMessage = obj.getMessage(Long.parseLong(messageId));
        return retrivedMessage;
    }

    /*
     * This method is used to add new message in database returns created
     * message with generated id
     */
    /*
     * @POST public Message postMessage(Message msg){
     * 
     * MessageService obj = new MessageService(); msg = obj.addMessage(msg);
     * 
     * return msg; }
     */

    /**
     * This method created the new message and sends new message with 201 status
     * code using Response
     * 
     * @param msg
     * @return
     */
    @POST
    public Response postMessage(Message msg, @Context UriInfo uriInfo) {

        MessageService obj = new MessageService();
        msg = obj.addMessage(msg);
        // This is to send only created status
        // return Response.status(Status.CREATED).entity(msg).build();

        // If we want to send status + new created resource path

        String newId = msg.getId() + "";
        URI uri = uriInfo.getAbsolutePathBuilder().path(newId).build();
        return Response.created(uri).entity(msg).build();
    }

    @PUT
    @Path("/{messageId}")
    public Message updateMessage(Message msg, @PathParam("messageId") long id) {

        MessageService obj = new MessageService();
        msg.setId(id);
        msg = obj.updateMessage(msg);

        return msg;
    }

    @DELETE
    @Path("/{messageId}")
    public void deleteMessage(@PathParam("messageId") long id) {

        MessageService obj = new MessageService();
        obj.removeMessage(id);
    }

    /**
     * This method is invoked if url asked for comments irrespective of http
     * method, it will invoke the CommentResource which is a sub resouce of
     * Message
     * 
     * @return
     */
    /*
     * @Path("/{messageId}/comments") public CommentResource test() { return new
     * CommentResource(); }
     */
}

I am having sub resource CommentResource as following:

package org.kaushik.javabrains.messenger.resource;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;

@Path("/")
public class CommentResource {

    public CommentResource() {
        // TODO Auto-generated constructor stub
    }

    @GET
    public String test() {

        return "new sub resource";
    }

    @GET
    @Path("/{commentId}")
    public String getCommentById(@PathParam("messageId") String messageId, @PathParam("commentId") String commentId) {

        return commentId + " This is dummy comment for messageID=" + messageId;
    }

}

I created a generic exception mapper, which is mapped to Throwable class. It is created for the purpose, if any exception happens at server end, rather displaying Tomcat error report page, meaningful message should be sent.

@Provider
public class GenericExceptionMapper implements ExceptionMapper<Throwable> {
    @Override
    public Response toResponse(Throwable ex) {
        System.out.println("in Genereic mapper");
        ErrorMessage message = new ErrorMessage(ex.getMessage(),"/Messenger/documentation",500);
        return Response.status(Status.INTERNAL_SERVER_ERROR).entity(message).build();
    }
}

Further, when I am testing my generic exception mapper by invoking a url

http://localhost:8080/messenger/webapi/messa

which is not mapped to any method in MessageResource. It is invoking the CommentResource following method:

@GET
@Path("/{commentId}")
public String getCommentById(@PathParam("messageId") String messageId, @PathParam("commentId") String commentId) {

    return commentId + " This is dummy comment for messageID=" + messageId;
}

If I comment the above method I am getting proper response in Postman.

Can anybody explain this behavior. Thanks in advance.


Solution

  • Remove @Path("/") from the CommentResource. It is being added as a root resource. So when you access /mesa, the CommentResource is mapped to /, and it hits the resource method mapped to @Path("{whatever}").

    Sub resource locator classes should not have @Path. That is only for root resource classes. In a sub-resource locator request, it will be ignored, but on app startup, it is still registered like a regular root resource.