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.
Related
I got an interface annotated with JAX-RS annotations.
In the implementation class itself I just override the methods of the interface; not overriding annotations or anything.
I get the following error:
The class {name} is an interface and cannot be instantiated.
I've tried making a jar of my annotated interface and put it in .war \lib folder, yet the error persists.
If it matters, I'm using JBoss's embedded Tomcat.
Here's the interface:
#javax.ws.rs.Path( "/jerseytesting.HelloWorldService" )
public interface HelloWorldService {
#javax.ws.rs.POST
#javax.ws.rs.Path( "/Greet" )
#javax.ws.rs.Consumes({"application/protobuf", "application/json"})
#javax.ws.rs.Produces({"application/protobuf", "application/json"})
jerseytesting.Twirpproto.HelloResponse greet(jerseytesting.Twirpproto.HelloRequest request);
}
And here is the implementation:
public class Twirpy implements Twirpproto.HelloWorldService {
#Override
public HelloResponse greet(HelloRequest request) {
HelloResponse helloResponse = HelloResponse.newBuilder().setResponse("Hello, " + request.getName()).build();
return helloResponse;
}
}
From the API documentation:
JAX-RS annotations MAY be used on the methods and method parameters of
a super-class or an implemented interface.
So, only the method annotations can be on the interface.
The concrete class should have the #Path. Only then JAX-RS will know to create an instance of that concrete class.
Like so:
#javax.ws.rs.Path( "/jerseytesting.HelloWorldService" )
public class Twirpy implements Twirpproto.HelloWorldService {
#Override
public HelloResponse greet(HelloRequest request) {
HelloResponse helloResponse = HelloResponse.newBuilder().setResponse("Hello, " + request.getName()).build();
return helloResponse;
}
}
tl;dr: I'm overriding a method annotation of an interface on a child-child class, but the annotation's value is not being override.
I'm surprised I didn't found the answer for this question on this or any other site, so I think it must be something wrong with my implementation, but I can't figure it out.
I have the following custom annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#Inherited //This only applies to class annotations, but I tried it anyway
public #interface MyRolesAllowed {
String[] value();
}
And it's used on this interface:
#Produces(MediaType.APPLICATION_JSON)
public interface IRESTfulCRUDResource<T> {
#GET
#Path("/")
#MyRolesAllowed("ADMIN")
public Response ws(#Context UriInfo uriInfo);
}
That's implemented as:
#Stateless
#Produces(MediaType.APPLICATION_JSON)
public abstract class BaseCRUDResource<T> implements IRESTfulCRUDResource<T> {
#Override
public Response wsQuery(#Context UriInfo uriInfo) {
//...
}
}
That I just override in this class to change the default annotation:
#Stateless
#Path("/something")
public class SomeResource extends BaseCRUDResource<Someclass> {
#Override
#MyRolesAllowed({"ADMIN", "USER"})
public Response wsQuery(#Context UriInfo uriInfo) {
return super.wsQuery(uriInfo);
}
}
This is all hosted on a Wildfly server, and I'm using the following interceptor to process the annotation:
#Provider
#Priority(Priorities.AUTHORIZATION)
public class AuthorizationInterceptor implements ContainerRequestFilter {
public void filter(ContainerRequestContext requestContext) throws IOException {
Method resourceMethod = resourceInfo.getResourceMethod();
// I checked and at this point:
// resourceInfo.getResourceClass().getSimpleName() == "SomeResource"
String[] roles = resourceMethod.getAnnotation(MyRolesAllowed.class).value();
// Now, I checked and at this point:
// roles = {"ADMIN"}
// In my understanding, it should be {"ADMIN", "USER"},
// because I'm overriding the annotation on the child
// class, and resourceMethod points to the child method.
// What's wrong?
}
}
So, as stated on the last comment, I was expecting the parent's annotation to be override by the child's one, but it's not happening. This question is an example of annotation's value override.
I tried to use resourceMethod.getAnnotationsByType as well, but it provides the same values.
Am I misunderstanding something?
Update: as #JohnBollinger pointed in the comments section, I checked and resourceMethod.getDeclaringClass() == IRESTfulCRUDResource, so it's counterintuitive but ResourceInfo's getResourceMethod points to the parent method but getResourceClass points to the child class.
This code works to access the uriInfo:
#Path("/testing")
public class Testing {
#javax.ws.rs.core.Context UriInfo uriInfo;
#POST
#Path("/test2")
#Produces(MediaType.TEXT_PLAIN)
public Response test2(
#FormParam("sessionId") String sessionId ) {
String currentUserId = Utils.setup(sessionId);
String accessPath = uriInfo.getAbsolutePath().toASCIIString();
System.out.println("The client used this URI to reach this resource method: " + uriInfo.getAbsolutePath().toASCIIString());
// Utils.test3("print this");
return Response.ok("Test 2 ended").build();
}
When I try to access the uriInfo in the called method Utils.test3("print this"); Here:
public class Utils {
#javax.ws.rs.core.Context static UriInfo uriInfo;
public static String setup(String sessionId) {
if (!com.retailapppartners.Utils.validSession(sessionId)) {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
String currentUserId = com.retailapppartners.Utils.getUserFromSession(sessionId);
MDC.put("user-id", currentUserId);
return currentUserId;
}
public static void test3(String message) {
System.out.println(message);
String path = uriInfo.getPath();
// System.out.println("The client used this URI: " + uriInfo.getAbsolutePath().toASCIIString());
return;
}
This fails with null pointer exception. I want to see the path uri in the called method to confirm security for all methods in my utils called method. I have searched hi and low for called examples of this. Thanks
Use the #Context annotation to inject an instance of UriInfo into an field variable or method parameter of your resource class
e.g. #1
public String find(#Context UriInfo uri){}
e.g. #2
public class RESTResource{
#Context
private UriInfo uri;
}
Continuing with my comment.. into an answer
Like I said, you can't just decide to inject it anywhere you want. The class being injected into needs to be managed by the JAX-RS runtime, as it's the one that will be doing the injecting. A resource class is managed, a filter provider is managed, that's why you can inject into them. You're utility class is not. And in any case, I don't think it would even work for a [static] "utility" class (even if you were to somehow get it managed) because of the static nature.
Let me just first mention, that UriInfo is specific to each request. static, by nature is "global". You cannot inject it as a static field.
One solution I can see is to make the Utils class (and methods) non-static, and use the underlying injection framework to inject an instance of the Utils class, where ever you need it. This way, if the Utils class is managed, then it should be able to inject the managed UriInfo instance. How this (getting the Utils class managed) will be achieved depends on the implementation you are using, and it's underlying injection framework.
For instance, with Jersey (2), I could do this
public class Utils {
#Context UriInfo uriInfo;
public String test(String s) {
return s + "=" +uriInfo.getAbsolutePath().toString();
}
}
#Path("some")
public class SomeResource {
#Inject
Utils utils;
#GET
public Response getSomething() {
return Response.ok(utils.test("Hello")).build();
}
}
public class JerseyApplication extends ResourceConfig {
public JerseyApplication() {
packages("stackoverflow.jersey.test");
register(new AbstractBinder(){
#Override
protected void configure() {
bind(Utils.class).to(Utils.class);
}
});
}
}
And this works just fine
C:\>curl -v http://localhost:8080/some
Result: Hello=http://localhost:8080/some
Jersey uses HK2 for its injection, so I am able to leverage it to injection of my Utils class.
Now this is probably not the answer you're looking for (as it defeats the purpose of a static utility class), but what you are trying too just can't be done. Either way you go, whether refactoring to pass the UriInfo to your static methods, or the solution above, you will probably have a lot of refactoring to do. I'm surprised you've already created 200 methods using this functionality, before making sure one worked :/
I'm developing REST API with Jersey as JAX-RS implementation.
In every resource I explicitly define expected parameters:
#GET
#Path("/someData")
public Response getSomeData(
#QueryParam("id") final Long id,
#QueryParam("name") final String name) {
...
}
There are a number of fixed parameters, which are common for all resources (e.g. "locale").
Is there any way (I'm ok with introducing Jersey-specific dependencies) I can forbid any parameters that belong neither to method parameters nor to the common parameters?
So for example if user invokes
/api/resource/someData?id=10&locale=en - he gets the data, but if he invokes
/api/resource/someData?id=10&locale=en&fakeParam=AAA - status 400 is returned, with content stating that fakeParam is unknown parameter.
Currently second request is processed the same way as the first one, and fakeParam is simply ignored.
I think described validation will help users of my API to spot bugs earlier.
I don't know of any way to do this with JAX-RS but you could easily roll your own solution. This is a bit cumbersome but you could do something like:
#Path("/api")
public class Service {
#Context
UriInfo uriInfo;
ImmutableSet<String> commonParams = ImmutableSet.of("locale");
#GET
#Path("validate")
#Produces(MediaType.APPLICATION_JSON)
public String validate(#QueryParam("foo") String param) {
Set<String> validParams = newHashSet(commonParams);
class Local {};
for (Annotation[] annotations: Local.class.getEnclosingMethod().getParameterAnnotations()) {
for (Annotation annotation: annotations) {
if (annotation instanceof QueryParam) {
validParams.add(((QueryParam)annotation).value());
}
}
}
if (!difference(uriInfo.getQueryParameters().keySet(), validParams).isEmpty()) {
//throw an unknown parameter exception
}
return "hello";
}
And if you're using Guice or some other AOP tool with Jersey you could probably put this into an aspect s.t. you wouldn't have to add boilerplate to every method you want to validate.
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
}
}