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.
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;
}
}
I am writing a custom Jersey 2.0 server logging filter.
Sample: https://github.com/jersey/jersey/blob/master/core-common/src/main/java/org/glassfish/jersey/logging/ServerLoggingFilter.java
A reference to ResourceInfo is injected as:
#Context
private ResourceInfo resourceInfo;
Interface ResourceInfo has two methods:
Class<?> getResourceClass()
Method getResourceMethod()
Is there ever a case where ResourceInfo.getResourceClass() != ResourceInfo.getResourceMethod().getDeclaringClass()?
Bonus question: Both methods may return null. Is it possible that only one would be null?
Can't say much about null values, but there is a case when getResourceClass() != getResourceMethod().getDeclaringClass() if you have some class hierarchy like superclass resource. It is easier to show in code, so consider this:
public class SuperResource {
#GET
#Produces({ MediaType.APPLICATION_JSON })
public Response getInfo() {
return Response.ok("{\"info\":1}").build();
}
}
#Path("test")
public class MyResource extends SuperResource{
// ... other endpoints
}
Now lets call
GET http://localhost:9998/test/
getResourceClass() : class ru.varren.MyResource
getResourceMethod(): javax.ws.rs.core.Response ru.varren.SuperResource.getInfo()
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.
I have a REST api written by someone else in which the method that handles the request to a particular url accepts a bunch of parameters that are populated from path parameters.
#POST
#Path("/{classid}/{studentid}/details")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#SuppressWarnings("unchecked")
public Response processFile(#FormDataParam("sourceFile") InputStream aStream, #PathParam("classid") String classId, #PathParam("studentid") String studentId, #Context HttpServletRequest httpRequest) {
// Code to do stuff and return a response
}
The person who wrote this has used DropWizard and I have no previous experience working on it. I have the task of validating the studentId field by comparing it with values in the db. This would be pretty straightforward but I have been told to do it using a custom validator. I am pretty new to writing annotations but after much digging wrote an annotation like this,
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Constraint(validatedBy = StudentIdValidator.StudentIdValidation.class)
public #interface StudentIdValidator {
String message() default "{Invalid Id}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
class StudentIdValidation implements ConstraintValidator<StudentIdValidator, String> {
#Override
public void initialize(StudentIdValidator constraintAnnotation) {
System.out.println("Annotation initialize !!!!!!!!!");
}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
// TODO Auto-generated method stub
System.out.println("Annotation called");
return false;
}
}
}
After this I added the annotation to the field I wanted to run the validation on like this,
public Response processFile(#FormDataParam("sourceFile") InputStream aStream, #PathParam("classid") String classId, #StudentIdValidator #PathParam("studentid") String studentId, #Context HttpServletRequest httpRequest)
Now the problem is that, when the I run/debug the code...this validator is not being called, also I have no idea how to get the value of studentId inside the studentId validation class. So I dug some more and added this to the application file
class MyApplication extends Application<MyConfiguration> {
........
#Override
public void run(MyConfiguration myConfiguration, Environment currentEnvironment) {
currentEnvironment.jersey().register(StudentIdValidator.class);
}
I am literally at the end of my wits. Any help will be very VERY appreciated. Sorry about the poor formatting.
this is pretty straight forward. I will paste my example here since I had it written up and I am lazy and don't want to take your fun experience away :)
Edit: I think your issue is that you didn't annotate your resource with #Valid
so here we go:
You are on the right track with the validator. These are mine:
public class CustomValidator implements ConstraintValidator<CustomValidation, String> {
#Override
public void initialize(CustomValidation constraintAnnotation) {
}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
System.out.println("Validation called");
return false;
}
}
And this is the Annotation:
#Constraint(validatedBy = {CustomValidator.class})
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Retention(value = RetentionPolicy.RUNTIME)
public #interface CustomValidation {
String message() default "Some message";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
The application:
public class Application extends io.dropwizard.Application<Configuration>{
#Override
public void run(Configuration configuration, Environment environment) throws Exception {
MetricRegistry metrics = environment.metrics();
environment.jersey().register(new HelloResource(metrics));
}
public static void main(String[] args) throws Exception {
new Application().run("server", "/home/artur/dev/repo/sandbox/src/main/resources/config/test.yaml");
}
}
And the resource:
#Path("/test")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class HelloResource {
private MetricRegistry service;
public HelloResource(MetricRegistry service) {
this.service = service;
}
#GET
public String hello() {
Timer timer = service.timer("test");
try(Context t = timer.time()) {
return "Hello World";
}
}
#GET
#Path("/test2")
public void test(#Valid #CustomValidation #QueryParam("arg") String test) {
System.out.println(test);
}
}
Don't mind the metrics, they have nothing to do with it. The important part is that you need to tell DW what you want to have validated.
In the resource, see the test method. I annotate the argument I need with #Valid (tells DW to validate) #CustomValidation (tells DW what validator to use).
This is not actually a Dropwizard feature, but rather a hibernate validator implementation.
The way it works under the hood is that hibernate creates the Validator class on the fly when requested by invoking the constructor. Now this works very fine if you have simple validation (like say comparing a string). If you need dependencies, then it gets slightly more tricky. I have an example for that as well, that you can read up on here:
With dropwizard validation, can I access the DB to insert a record
This example uses guice, but it demonstrates how you can hook your own creation mechanism into validation. That way you can control your validator creation and inject or initialise them with a datasource to access your database.
I hope that answers your questions,
Artur
Custom valdation in drop wizard are same as hibernate custom validators.
Follow the link : hibernate validator-customconstraints
I want to intercept any class or methods annotated with #Foo
Class level interception:
#Foo
#path("/foo")
public class Attack {...}
Method level interception:
#path("/bar")
public class defend {
#Foo
#GET
public String myMethod(){....}
I want to intercept any class or methods annotated with #Foo but not other methods or classes. I want to print out the entire path or URI before proceeding to the method execution. One the method call is finished, I want to print out "executed sucessfully"
That is something of this kind:
system.out.println(path) // this is the path the request is made. something like /api/2/imp/foo
method call happens
method call finishes
System.out.println("executed successfully")
My scenario is different but this is the fundamental problem I have. I do not want to be implementation specific. Java EE 7 specification has a way to do this using #Postconstruct, #AroundInvoke etc. But I am really having difficulty assembling this.
This post is definitely a great approach to this problem. but it is implementation specific (RESTeasy) and the AcceptByMethod it uses is deprecated.
Thanks
Skimming through the Java EE Tutorial for JAX-RS, it seems they fail to mention anything about the concept of Filters and Interceptors from the jsr339-jaxrs-2.0-final-spec. You should probably download a copy for complete information.
Filters and entity interceptors can be registered for execution at well-defined extension points in JAX-RS
implementations. They are used to extend an implementation in order to provide capabilities such as logging,
confidentiality, authentication, entity compression
Entity interceptors wrap around a method invocation at a specific extension point. Filters execute code at an extension point but without wrapping a method invocation.
Basically the last paragraph is saying that interceptors occur in the same execution stack as the method call, whereas filters don't. That doesn't mean we can't use filters for your logging case. The filter contexts passed to the filter interface method actually have a lot more information that you can use.
The ContainerRequestFilter and ContainerResponseFilter get passed ContainerRequestContext and ContainerResponseContext, respectively, of which we can obtain things like the UriInfo to get the path from.
public interface ContainerResponseFilter {
void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext)
}
public interface ContainerRequestFilter {
void filter(ContainerRequestContext requestContext)
}
Here's a simple example of a logging filter. There a few different ways to bind the filter, but with this example I'll use dynamic binding where I instantiate the filter explicitly, Therefore I don't have a container managed state, and pass the class and method names to the filter
public class LoggingFilter implements ContainerRequestFilter,
ContainerResponseFilter {
private static final Logger logger
= Logger.getLogger(LoggingFilter.class.getName());
protected String className;
protected String methodName;
public NewLoggingFilter(String className, String methodName) {
this.className = className;
this.methodName = methodName;
}
#Override
public void filter(ContainerRequestContext requestContext)
throws IOException {
logger.log(Level.INFO, "Request path: {0}",
requestContext.getUriInfo().getAbsolutePath().toString());
logger.log(Level.INFO, "Starting Method: {0}.{1}",
new Object[]{className, methodName});
}
#Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext)
throws IOException {
logger.log(Level.INFO, "Finished Method: {0}.{1}",
new Object[]{className, methodName});
}
}
Here's how I bind the methods to the filter. Every resource method goes through this binder. If it or it's class is annotation with our custom annotation, it will be binded to out LoggingFilter. We also pass the LogginFilter the class and method names of the resource method. We will use those names for our logging
#Provider
public class LoggingBinder implements DynamicFeature {
#Override
public void configure(ResourceInfo ri, FeatureContext fc) {
Class<?> clazz = ri.getResourceClass();
Method method = ri.getResourceMethod();
if (method.isAnnotationPresent(Logged.class)
|| clazz.isAnnotationPresent(Logged.class)) {
fc.register(new LoggingFilter(clazz.getName(), method.getName()));
}
}
}
It checks the method or class to see if it has the annotation #Logged (which is a custom annotation - you can just as easily call it #Foo)
#NameBinding
#Retention(RUNTIME)
#Target({METHOD, TYPE})
public #interface Logged {
}
Using this resource class
#Path("/log")
public class LogResource {
#GET
#Logged
public Response getLoggingResourceMethod() {
return Response.ok("Hello Logging Response").build();
}
}
We get the following result in our log
Oct 25, 2014 4:36:05 PM jaxrs.stackoverflow.filter.NewLoggingFilter filter
INFO: Request path: http://localhost:8081/rest/log
Oct 25, 2014 4:36:05 PM jaxrs.stackoverflow.filter.NewLoggingFilter filter
INFO: Starting Method: jaxrs.stackoverflow.filter.LogResource.getLoggingResourceMethod
Oct 25, 2014 4:36:05 PM jaxrs.stackoverflow.filter.NewLoggingFilter filter
INFO: Finished Method: jaxrs.stackoverflow.filter.LogResource.getLoggingResourceMethod
Oct 25, 2014 4:36:05 PM jaxrs.stackoverflow.filter.NewLoggingFilter filter
INFO: Method successful.
Don't forget to download the spec for more details.
An Interceptor is really simple:
#Foo #Interceptor
public class FooInterceptor
{
#AroundInvoke
public Object handleFoo(InvocationContext joinPoint) throws Exception
{
Method m = joinPoint.getMethod();
// you can access all annotations on your #Foo-annotated method,
// not just the #Foo annotation.
Annotation[] as = m.getDeclaredAnnotations();
// do stuff before the method call
...
try
{
// here you call the actual method
return joinPoint.proceed();
}
finally
{
// do stuff after the method call
...
}
}
}
This is how the annotation would look:
#InterceptorBinding
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD, ElementType.TYPE })
public #interface Foo
{
#Nonbinding
... // you could add parameters for your annotation, e.g. #Foo(value)
}
And this is how you would use it:
#Stateless
public class MyService
{
#Foo("bar")
public String myWrappedMethod()
{
...
}
}
The code inside myWrappedMethod would be "wrapped" by the code in the FooInterceptor.
Note that the interceptor is only invoked if the method call to myWrappedMethod() is managed by the container, i.e. you invoke it on a managed instance of MyService (e.g. via #Inject)