JAX-RS multiple classes with the same path - java

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
}
}

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.

Can we have an empty base path?

Can we have an empty basepath for #Path annotation?
ie. #Path("/")
I want to provide REST api http://servername/abc
#Path("")
public class YourResource {
#Path("/abc")
#GET
public Responce method1(){
return Response.ok("ok").build();
}
}
When I do this, exception is thrown
javax.ws.rs.WebApplicationException
at com.sun.jersey.server.impl.uri.rules.TerminatingRule.accept(TerminatingRule.java:66)
at com.sun.jersey.server.impl.uri.rules.ResourceClassRule.accept(ResourceClassRule.java:108)
at com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147)
UPDATE - I bind in my GuiceServletContextListener like below
bind(YourResource.class).in(Singleton.class);
serve("/abc").with(GuiceContainer.class);
Remove #Path("/") annotation completely from class
and
prefix slash (/) into the method level path annotation, like #Path("/abc")
Please verify whether your URI is mapped to java method as below.
While starting your server, you can see how the URI's mapped into the java methods, something like this in eclipse console...
Mapped "{[/static/transit],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public java.util.List<com.xxx.yyy.zzz.domain.Transit> com.xx.yy.zz.controller.abcDataController.getAllTransit()
==================================================================
UPDATE:
According to the java doc below, There are no difference between these 2 examples, both will work.
Either you can use leading slash with both URIs or don't use any leading slash with both URIs, both are same.
Paths are relative. For an annotated class the base URI is the
application path, see ApplicationPath. For an annotated method the
base URI is the effective URI of the containing class. For the
purposes of absolutizing a path against the base URI , a leading '/'
in a path is ignored and base URIs are treated as if they ended in
'/'.
#Path("message")
public class MessageServices {
#PUT
#Path("sendsms")
#Consumes(MediaType.APPLICATION_JSON)
#Produces({MediaType.APPLICATION_JSON})
public Response sendSms() {
//....
}
}
OR
#Path("/message")
public class MessageServices {
#PUT
#Path("/sendsms")
#Consumes(MediaType.APPLICATION_JSON)
#Produces({MediaType.APPLICATION_JSON})
public Response sendSms() {
//....
}
}
add a slash to your path ("/"), like this:
#Path("/")
public class YourResource {
...
}

is it possible to call one jax-rs method from another?

suppose i have some jax-rs resource class:
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class ResourceA {
#GET
public Something get(#Context UriInfo uriInfo) {
if (...) {
//how to get to ResourceB ?
}
}
}
and i want to conditionally redirect the call to some other jax-rs resource:
public class ResourceB {
#GET
#Path("{identifier}")
public Other get(#PathParam("identifier")String someArg) {
}
}
how do i do this?
note that i dont want this to be visible to the client (so no http redirects) and generally the resource methods i want to redirect to dont share the same signature (they may have path params etc as in the example i gave).
im running jersey 2.6 under apache tomcat (its a spring app, if thats any help)
EDIT - im looking for a jax-rs equivalent of servlet forward. i dont want to do an extra http hop or worry abour instantiating resource classes myself
You can get it using ResourceContext as follows:
#Context
ResourceContext resourceContext;
This will inject the ResourceContext into your Resource. You then get the resource you want using:
ResourceB b = resourceContext.getResource(ResourceB.class);
The Javadoc for ResourceContext is here. You can find a similar question here
I'm not aware of any possibility to do this from a resource method, but if it fits your use case, what you could do is implement your redirect logic in a pre matching request filter, for example like so:
#Provider
#PreMatching
public class RedirectFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) {
UriInfo uriInfo = requestContext.getUriInfo();
String prefix = "/redirect";
String path = uriInfo.getRequestUri().getPath();
if (path.startsWith(prefix)) {
String newPath = path.substring(prefix.length());
URI newRequestURI = uriInfo.getBaseUriBuilder().path(newPath).build();
requestContext.setRequestUri(newRequestURI);
}
}
}
This will redirect every request to /redirect/some/resource to /some/resource (or whatever you pass to requestContext.setRequestUri()) internally, before the resource method has been matched to the request and is executed and without http redirects or an additional internal http request.

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 paths

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

Categories