Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get the JAX-RS @Path of a different resource during a POST?

I have two REST classes for a simple web service (Jersey and GlassFish) that involves user resources - one to operate on all users (e.g., a factory for @POSTing) and another on individual users (e.g., @GET, @PUT, @DELETE). They are at:

@Stateless @Path("users") public class AllUsersResource {...}
@Stateless @Path("user") public class OneUserResource {...}

respectively. When POSTing to AllUsersResource I want to return the Location (via Response.created(uri).build()) of the new User, e.g.,

http://localhost:8080/.../user/152

My question is how to do this. AllUsersResource injects @Context UriInfo uriInfo, but that does not get me @Path info for OneUserResource, only that of the current call ("users"). The way I finally got it working was simply to use reflection, but I'm worried it is brittle and unclean:

OneUserResource.class.getAnnotation(Path.class).value();

Searching StackOverflow the only other things I found to try were the following, without success:

  • com.sun.jersey.api.core.ResourceContext
  • javax.ws.rs.core.UriInfo.getMatchedResources()
  • @javax.inject.Inject OneUserResource oneUserRes;

Any help would be terrific!

like image 426
Matthew Cornell Avatar asked Feb 23 '12 17:02

Matthew Cornell


4 Answers

You can use UriBuilder.fromresource(), but this only works if the supplied Resource class is a root resource (this is clearly mentioned in the javadocs). I found a way to achieve this even if you are in a sub-resource class:

@POST
@Consumes({MediaType.APPLICATION-XML, MediaType.APPLICATION-JSON})
public Response createUser(final User user, @Context UriInfo uriInfo) {
    // persist the user here
    URI uri = uriInfo.getAbsolutePathBuilder().path(user.getId()).build();
    return Response.created(uri).build();
}
like image 108
curioustechizen Avatar answered Oct 13 '22 16:10

curioustechizen


I found a couple of javax.ws.rs.core.UriBuilder methods that did the trick, which I wanted to share in case others had this question. They are: UriBuilder.fromResource(OneUserResource.class) and javax.ws.rs.core.UriBuilder.path(Class). I used the latter in a one-shot call:

URI newUserUri = uriInfo.getBaseUriBuilder().path(OneUserResource.class).path("/" + user.getId()).build();
return Response.created(newUserUri).build();
like image 25
Matthew Cornell Avatar answered Oct 13 '22 16:10

Matthew Cornell


With the strict REST concept you can make it as one root resource

@POST   /users        -> CREATE a single user
@GET    /users        -> READ all users
@PUT    /users        -> UPDATE (REPLACE) all users @@?
@DELETE /users        -> DELETE all users @@?

@POST   /users/{id}   -> CREATE a single user's some other child; @@?
@GET    /users/{id}   -> READ a single user
@PUT    /users/{id}   -> UPDATE a single user
@DELETE /users/{id}   -> DELETE a single user
@Path("/users")
@Stateless
public class UsersResouce {

    // /users
    @POST
    @Consumes({MediaType.APPLICATION-XML, MediaType.APPLICATION-JSON})
    public Response createUser(final User user) {
        // persist the user here
        return Response.created("/" + user.getId()).build();
    }

    // /users
    @GET
    @Produces({MediaType.APPLICATION-XML, MediaType.APPLICATION-JSON})
    public Response readUsers() {
        //return all users
    }

    // /users/{id}
    @GET
    @Path("/{user_id: \\d+}")
    @Produces({MediaType.APPLICATION-XML, MediaType.APPLICATION-JSON})
    public Response readUser(
        @PathParam("user_id") final Long userId) {

        final User persisted = userBean.find(userId);

        if (persisted == null) {
            return Response.status(Status.NOT_FOUND).build();
        }

        return Response.ok().entity(persisted).build();
    }

    // /users/{id}
    @Consumes({MediaType.APPLICATION-XML, MediaType.APPLICATION-JSON})
    @PUT
    @Path("/{user_id: \\d+}")
    public Response updateUser(
        @PathParam("user_id") final Long userId,
        final User mergeable) {

        final User persisted = userBean.find(userId);

        if (persisted == null) {
            userBean.persist(mergeable);
        } else {
            persist.setName(mergeable.getName());
            userBean.merge(persisted);
        }

        return Response.status(Status.NO_CONTENT).build();
    }

    // /users/{id}
    @DELETE
    @Path("/{user_id: \\d+}")
    public Response deleteUser(
        @PathParam("user_id") final Long userId) {

        userBean.delete(userId);

        return Response.status(Status.NO_CONTENT).build();
    }

    @EJB
    private UserBean userBean;
}
like image 5
Jin Kwon Avatar answered Oct 13 '22 15:10

Jin Kwon


As of JAX-RS 2.0, the most correct way (As far as I know) is to use the builder method like so:

    String uri = uriInfo.getBaseUriBuilder()
      .path(ODataV4Endpoint.class)
      .path(ODataV4Endpoint.class, "serviceEndpointJSONCatalog")
      .resolveTemplate("endpointId", endpointId).build().toString();

FYI, I need to call path twice in my case, once for the path annotation on the class, and the second time for the annotation on the method. I suspected the call to the method would do both, but it does not.

The Path annotation on the endpoint serviceEndpointJSONCatalog declared a parameter, like so: 'endpoint/{endpointId}', so the call to resolveTemplate was needed. Otherwise you would just call path(Class cl, String method).

In my case I created a builder and a symbolic way to reference the methods so the compiler / runtime could check them.

like image 1
absmiths Avatar answered Oct 13 '22 15:10

absmiths