I created a Jersey filter and I need it assigned to some resources (not all). Therefore, I'm using dynamic binding to achieve that.
public class MyDynamicFeature implements DynamicFeature {
@Override
public void configure(ResourceInfo resourceInfo, FeatureContext featureContext) {
Path resourcePath = resourceInfo.getResourceClass().getAnnotation(Path.class);
if (resourcePath != null && resourcePath.value().contains("/v2/"))
{
featureContext.register(MyFilter.class);
}
}
}
So I want this filter to be applied to all methods in those resources that contain a certain string in their paths. Some of those resources use sub-resource locators to define sub-resources. Eg,
@Path("/v2/resource_path")
@Consumes({ ... })
@Produces({ ... })
class MyResource
{
@Path("/subresource_path")
public MySubResource getSubResource(@Context ResourceContext rc)
{
return rc.getResource(MySubResource.class);
}
}
Even though Jersey documentation claims
The configure method will be executed once for each resource method that is defined in the application.
the configure
method in MyDynamicFeature
shown above doesn't get called for getSubResource
method of MyResource
class at all. It does get called for all the rest of the methods in MyResource
class though (which I omitted in the example).
Is there a way to make this work for sub-resources? I need my filter to be applied to MySubResource
as well.
We use Jersey 2.21.
Check out this issue . I'm not sure that it is currently possible. If you add some logging in your feature to log the method and class, you will see that subresource methods are never traversed. As explained by Marek in the issue, it's because in order to handle this, the sub-resource locator method would need to be invoked, which it never is.
The only workaround is to use Name Binding
instead. I've tested this and it works (see below). The idea is to make a custom annotation, and annotate the filter, the resource class, and sub-resource class you want filtered. For example
@NameBinding
@Target({METHOD, TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeAnno {}
@SomeAnno
public class Filter implements ContainerRequestFilter {}
@SomeAnno
@Path("v2")
public class V2Resource {
@Path("sub")
public V2SubResource get() {
return new V2SubResource();
}
@SomeAnno
public static class V2SubResource {
@GET
public String get() { return "get"; }
}
}
The above would bind all the resource methods in V2Resource
as well as V2SubResource
.
Below is a complete example using Jersey Test Framework . Run it like any other JUnit test
UPDATE : Note that with the below tests, with the current (2.26) version of Jersey, the tests for the version 2 resource hangs because of the 1000 status code being returned in the filter. I guess Jersey doesn't like this. To fix this just change the status code in the filter to 500 and fix the test assertions accordingly to test for a 500 status code.
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.logging.Logger;
import javax.ws.rs.GET;
import javax.ws.rs.NameBinding;
import javax.ws.rs.Path;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
/**
* Stack Overflow question http://stackoverflow.com/q/36878817/2587435
*
* Run this like any other JUnit test. Only one required test dependency
*
* <dependency>
* <groupId>org.glassfish.jersey.test-framework.providers</groupId>
* <artifactId>jersey-test-framework-provider-inmemory</artifactId>
* <version>${jersey2.version}</version>
* </dependency>
*
* @author Paul Samsotha
*/
public class DynamicSubresourceTest extends JerseyTest {
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public static @interface Status1000 {}
@Provider
@Status1000
public static class Status1000Filter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext context) throws IOException {
context.abortWith(Response.status(500).build());
}
}
@Path("v1")
public static class V1Resource {
@GET
public String get() {
return "v1";
}
@Path("sub")
public V1SubResource getSub() {
return new V1SubResource();
}
public static class V1SubResource {
@GET
public String get() {
return "v1subresource";
}
}
}
@Path("v2")
@Status1000
public static class V2Resource {
@GET
public String get() {
return "v2";
}
@Path("sub")
public V2SubResource getSub() {
return new V2SubResource();
}
@Status1000
public static class V2SubResource {
@GET
public String get() {
return "v2subresource";
}
}
}
@Override
public ResourceConfig configure() {
return new ResourceConfig(V1Resource.class, V2Resource.class)
.property(ServerProperties.WADL_FEATURE_DISABLE, true)
.register(Status1000Filter.class)
.register(new LoggingFilter(Logger.getAnonymousLogger(), true));
}
@Test
public void should_return_1000_for_v2_resource_method() {
final Response response = target("v2").request().get();
assertThat(response.getStatus(), is(500));
}
@Test
public void should_return_1000_for_v2_subresource_locator() {
final Response response = target("v2/sub").request().get();
assertThat(response.getStatus(), is(500));
}
@Test
public void should_return_data_for_v1_resource_method() {
final Response response = target("v1").request().get();
assertThat(response.getStatus(), is(200));
assertThat(response.readEntity(String.class), is("v1"));
}
@Test
public void should_return_data_for_v1_subresource_locator() {
final Response response = target("v1/sub").request().get();
assertThat(response.getStatus(), is(200));
assertThat(response.readEntity(String.class), is("v1subresource"));
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.