JAX-RS: Multiple paths - java

Is it possible to do something like that?
import javax.ws.rs.GET;
import javax.ws.rs.Path;
public class xxx
{
#GET
#Path(value = "path1")
public Response m1()
{
...
}
#GET
#Path(value = "path2")
public Response m1()
{
...
}
}
I'm using RESTEasy btw.

#Path("/{a:path1|path2}")
From resteasy docs:
http://docs.jboss.org/resteasy/docs/1.0.2.GA/userguide/html_single/index.html#_Path_and_regular_expression_mappings

yes you can do that although you will have to rename your methods so that their signature is different.
Update: Check Dieter Cailliau's answer, #Path("/{a:path1|path2}") is probably what you want...
public class BlahResource{
#GET
#Path("path1")
public Response m1(){
return Response.ok("blah").build();
}
#GET
#Path("path2")
public Response m2(){
return this.m1();
}
you can check JSR-311's API and it's reference implementation named "jersey" there:
JSR311 API
Jersey

Some extra details about Path annotation...
As a previous responses state, regular expressions to be used with in the annotated path declaration mapping:
{" variable-name [ ":" regular-expression ] "}
You can declare multiple paths, but there is also a path hierarchy that was not immediately obvious to me whereby the class annotated path prefixes the following method path annotations. One might write the following class for a concise multiple path option which could be useful for resource versioning perhaps.
#Path("/{a:v1|v2}")
#Produces("text/*")
public class BlahResource {
#GET
#Path("/blah")
public Response m1() {
return Response.ok("blah").build();
}
}
Please note the fact that the class "BlahResource" has been declared with the path "/v1" or "/v2" making the resource accessible as:
$ curl localhost:8080/v1/blah
blah
and also
$ curl localhost:8080/v2/blah
blah

You could use sub resources to map two paths to the same resource:
public class MySubResource {
#GET
public Response m1() {
return Response.ok("blah").build();
}
}
#Path("/root")
public class MyRootResource {
#Path("/path1")
public MySubResource path1() {
return new MySubResource();
}
#Path("/path2")
public MySubResource path2() {
return new MySubResource();
}
}

Related

Grizzly REST - POSTs are always 404

In my endpoint, I have some methods with #GET and some methods with #POST. #GETs are working fine, but #POSTs always return 404.
Here is some part from the endpoint's interface:
public interface TestEndpoint {
#GET
#Path("/ping")
Response ping();
#POST
#Path("/weather/{iata}/{pointType}")
Response updateWeather(#PathParam("iata") String iataCode,
#PathParam("pointType") String pointType,
String datapointJson);
#POST
#Path("/airport/{iata}/{lat}/{long}")
Response addAirport(#PathParam("iata") String iata,
#PathParam("lat") String latString,
#PathParam("long") String longString);
#GET
#Path("/exit")
Response exit();
}
Here is the server initialization part:
public class TestServer {
private static final String BASE_URL = "http://localhost:9090/";
public static void main(String[] args) {
try {
final ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.register(TestEndpointImpl.class);
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URL), resourceConfig, false);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
server.shutdownNow();
}));
HttpServerProbe probe = new HttpServerProbe.Adapter() {
public void onRequestReceiveEvent(HttpServerFilter filter, Connection connection, Request request) {
System.out.println(request.getRequestURI());
}
};
server.getServerConfiguration().getMonitoringConfig().getWebServerConfig().addProbes(probe);
server.start();
Thread.currentThread().join();
server.shutdown();
} catch (IOException | InterruptedException ex) {
Logger.getLogger(TestServer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
where, TestEndpointImpl is an implementation of TestEndpoint (as the name implies) with class-level annotation #Path("/collect").
When I perform GET requests, it works fine. But POSTs are problematic. Corresponding methods are not called.
As a side note, probe prints both GET and POST requests as expected, so I am sure that requests reach the server and paths are ok.
Is there any suggestion?
EDIT: Some snippet from the implementation:
#Path("/collect")
public class TestEndpointImpl implements TestEndpoint {
...
#Override
public Response updateWeather(#PathParam("iata") String iataCode, #PathParam("pointType") String pointType,
String datapointJson) {
System.out.println("TRACE: " + datapointJson);
// do something and return a Response
}
...
}
The registered probe prints /collect/weather/BOS/wind, but updateWeather is not called.
Short answer
Copy the #POST and the #Path annotations to the method implementation. It will do the trick.
Long answer
The section regarding annotation inheritance of the JAX-RS 2.0 specification (the specification which Jersey is the reference implementation) is pretty clear. See the quote below:
3.6 Annotation Inheritance
JAX-RS annotations may be used on the methods and method parameters of a super-class or an implemented interface. Such annotations are inherited by a corresponding sub-class or implementation class method provided that the method and its parameters do not have any JAX-RS annotations of their own. Annotations on a super-class take precedence over those on an implemented interface. The precedence over conflicting annotations defined in multiple implemented interfaces is implementation specific. Note that inheritance of class or interface annotations is not supported.
If a subclass or implementation method has any JAX-RS annotations then all of the annotations on the superclass or interface method are ignored. E.g.:
public interface ReadOnlyAtomFeed {
#GET
#Produces("application/atom+xml")
Feed getFeed();
}
#Path("feed")
public class ActivityLog implements ReadOnlyAtomFeed {
public Feed getFeed() {...}
}
In the above, ActivityLog.getFeed inherits the #GET and #Produces annotations from the interface. Conversely:
#Path("feed")
public class ActivityLog implements ReadOnlyAtomFeed {
#Produces("application/atom+xml")
public Feed getFeed() {...}
}
In the above, the #GET annotation on ReadOnlyAtomFeed.getFeed is not inherited by ActivityLog.getFeed and it would require its own request method designator since it redefines the #Produces annotation.
For consistency with other Java EE specifications, it is recommended to always repeat annotations instead of relying on annotation inheritance.
That can also happen if the url is not in the correct format; for example you could have sent a request without the correct path parameters.

How to reuse a REST methods in different resources?

Hi,
I am building a REST-api using Jersey and Java. I wonder if it is possible to reuse a method in many resources.
As an example If I have this code:
#Path("/users")
public class UserResource {
#GET
#Path("/{uid}/comments")
#Produces(MediaType.APPLICATION_JSON)
public List<Comment> getComments() {
return commentService.getOnEntity("User", uid);
}
}
and this:
#Path("/items")
public class ItemResource {
#GET
#Path("/{uid}/comments")
#Produces(MediaType.APPLICATION_JSON)
public List<Comment> getComments() {
return commentService.getOnEntity("Item", uid);
}
}
Is it possible to reuse the code for specifying the method "/{uid}/comments/" so I do not need to write it in every resource that is going to need it?
I guess I could extend a CommentResource with the method, but the I can only add one set of methods. If I use Interface I could specify more than one set of methods but would have to rewrite the code inside the methods in every resource.
Edit
After a tips from #thomas.mc.work I rewrote my code using a sub resource. It is better than the first solution since I get all methods from my sub resource and it only takes 4 lines of code per resource. This is how it looks like:
#Path("/users")
public class UserResource {
#Path("/{uid}/comments")
public CommentSubResource getCommentSubResource(#PathParam("uid") String uid) {
return new CommentSubResource("User", uid);
}
}
and this:
#Path("/items")
public class ItemResource {
#Path("/{uid}/comments")
public CommentSubResource getCommentSubResource(#PathParam("uid") String uid) {
return new CommentSubResource("Item", uid);
}
}
and this:
public class CommentSubResource {
private String entity;
private String entityUid;
public CommentSubResource(String entity, String entityUid) {
this.entity = entity;
this.entityUid = entityUid;
}
#GET
#Path("/")
#Produces(MediaType.APPLICATION_JSON)
public List<Comment> getComments() {
return commentService.getOnEntity(entity, entityUid);
}
#DELETE
#Path("/")
#Produces(MediaType.APPLICATION_JSON)
public List<Comment> deleteComment(#PathParam("uid") String uid) {
return commentService.delete(uid);
}
}
This is much better. I have an idea to use java 8 and default implementation interfaces to be able to just implmenet an interface to get the functionality, but I am not sure if I am able to determine which resource the default implemented method is called from.
Edit
After some laboration I think subresources is the way to go, even if it´s not (according to me) the perfect solution.
There is a similar feature called "Subresource Locators". You can decide in runtime which Resource is selected to process the request that is matching your JAX-RS method.

JAX-RS: OPTIONS for every Resource

I am using a JAX-RS interface with XMLHttpRequest (XHR). Due to the XHR preflight, XHR send always OPTIONS before calling the real resource.
Now I have dozens of methods and I need the OPTIONS for every resoruce. Is there any way to do this automatically? I dont want to write dozens of methods like:
#OPTIONS
#Path("/{id}")
#PermitAll
public Response optionsById() {
return Response.status(Response.Status.NO_CONTENT).build();
}
#OPTIONS
#Path("/{id}/data")
#PermitAll
public Response optionsByData() {
return Response.status(Response.Status.NO_CONTENT).build();
}
UPDATE 09/12/2013: THIS DOES NOT WORK. Using this all #GET/#DELETE/#POST/#PUT are not working any more.
Finally I solved my problem. I created a super class OptionsResource, from which all resources inherit. This resoruce contains:
// Match root-resources
#OPTIONS
#PermitAll
public Response options() {
return Response.status(Response.Status.NO_CONTENT).build();
}
// Match sub-resources
#OPTIONS
#Path("{path:.*}")
#PermitAll
public Response optionsAll(#PathParam("path") String path) {
return Response.status(Response.Status.NO_CONTENT).build();
}
An example:
#Path("/test")
public class TestResource extends OptionsResource {
#GET
#Produces("text/plain;charset=UTF-8")
public Response index() {
return Response.status(Status.OK).entity("works").build();
}
}
This matches:
curl -I -X OPTIONS http://myhost.com/test
curl -I -X OPTIONS http://myhost.com/test/asd/aasd/12/
etc.
Quite a late reply, but a much nicer solution is to use a filter that catches all the OPTIONS call before path matching. In Kotlin, it will look like this:
#Provider #PreMatching
class OptionsFilter: ContainerRequestFilter {
override fun filter(requestContext: ContainerRequestContext) {
if (requestContext.method == "OPTIONS") {
requestContext.abortWith(Response.status(Response.Status.NO_CONTENT).build())
}
}
}
The java version:
#Provider
#PreMatching
public class OptionFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
if (requestContext.getMethod().contentEquals("OPTIONS")) {
requestContext.abortWith(Response.status(Response.Status.NO_CONTENT).build());
}
}
}

jersey is not finding my resource with sub url

I want to have two resources at URLs: /apps and /apps/runs.
So, I created resources as shown below. I use Spring for object injection. When I use this way, I am getting the 404 error for HTTP get requests on /apps/runs. Am I doing some thing wrong?
Here is my code:
#Scope("prototype")
#Path("/apps")
public class ManufacturersResource {
#GET
#Produces("application/xml")
public List<Applications> getApplications() {
return apps.findAll();
}
}
#Scope("prototype")
#Path("/apps/runs")
public class ManufacturersResource {
#GET
#Produces("application/xml")
public List<ApplicationInstances> getApplicationInstances() {
return appInstances.findAll();
}
}
Jersey won't allow you to have two files share a common prefix, if one is using the prefix as an entire resource url.
So you can move both methods inside the same file, or have /apps be something else like /apps/list

JAX-RS multiple classes with the same path

With JAX-RS, is it possible to have more than one class assigned to a single path? I'm trying to do something like this:
#Path("/foo")
public class GetHandler {
#GET
public Response handleGet() { ...
}
#Path("/foo")
public class PostHandler {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response handlePost() { ...
}
This apparently isn't allowed as I get:
com.sun.jersey.api.container.ContainerException: A root resource, class PostHandler, has a non-unique URI template /foo
I can always create one class to handle requests and then delegate to helper classes. I was hoping there was a standard way of doing so.
The JAX-RS spec doesn't forbid such a mapping. For example, Resteasy JAX-RS implementation allows for it. The feature should be jersey specific.
Regarding:
I can always create one class to handle requests and then delegate to helper classes. I was hoping there was a standard way of doing so.
Usually you have the resource classes with the same name as the path:
#Path("/foo")
public class FooResource {
#GET
#Path("/{someFooId}")
public Response handleGet() {
...
}
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response handlePost() {
...
}
}
You cannot have multiple resources mapped to the same path. I tried that few days back and landed up at similar error.
I ended up doing subpaths such as /api/contacts for one resource and /api/tags for another.
The only other long way is to create resources in multiple packages and then create different app for each.
I had the similar issue, making the class level #PATH annotation to empty string and moving the resource name to method level #PATH annotation fixed this issue.
#Path("")
public class GetHandler {
#GET
#Path("/foo")
public Response handleGet() {
// impl
}
}
#Path("")
public class PostHandler {
#POST
#Path("/foo")
#Consumes(MediaType.APPLICATION_JSON)
public Response handlePost() {
// impl
}
}

Categories