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
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();
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();
}
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;
}
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.