3
votes

Servlet 3.0-enabled containers allows us to skip the web.xml servlet configuration and automatically scan your code for resources and providers once you extend javax.ws.rs.core.Application, annotate it with @ApplicationPath and do not override the getClasses() method. (hope I got all of that right :\)

At the moment I am using the Jersey implementation and securing resource methods using @RolesAllowed annotations. For this I need to register the org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature Provider class, however, the only ways I'm aware of to do this is either to:

  1. Register the class in the getClasses() method of my Application class (which, I think, will cause the Servlet 3.0 container NOT to auto-scan)
  2. Continue to use the web.xml Jersey servlet setup with

    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <param-value>org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature</param-value>
    </init-param>
    

Now the context behind this question is that I might have to switch to using RESTeasy and if I use option 1 it adds a Jersey dependency in the code and the code is no longer generic.

How do I write my code to use security annotations while maintaining generic JAX-RS code that could be deployed to another Servlet 3.0 JAX-RS implementation?

1

1 Answers

4
votes

One option is to use a javax.ws.rs.core.Feature (a JAX-RS standard class). You can register any components there, and then annotate the class with @Provider, and it will be picked up like any other @Provider or @Path annotated class

@Provider
public class MyFeature implements Feature {
    @Overrride
    public boolean configure(FeatureContext context) {
        context.register(RolesAllowedDynamicFeature.class);
    }
}

Do note that since you are using the Jersey feature, your app is no longer implementation independent, so you might as well use Jersey all the way. For one, Jersey does not recommend scanning the class-path, which is the affect of doing what you are doing. Instead Jersey has a mechanism that allows you to recursively scan a package (and its sub-packages). So you could instead do

@ApplicationPath("..")
public class AppConfig extends ResourceConfig {
    public AppConfig() {
        packages("the.packages.to.scan");
        register(RolesAllowedDynamicFeature.class);
    }
}

Note that ResourceConfig is a sub-class of Application

See Also:

Note:

If you wanted to stick to the classpath scanning mechanism, and wanted to keep the project independent of any Jersey dependencies, you could also override Map<String, Object> getProperties() in the Application class. In the returned Map, you could add the property that you would otherwis have added in the web.xml

@Override
public Map<String, Object> getProperties() {
    Map<String, Object> props = new HashMap<>();
    props.put("jersey.config.server.provider.classnames",
              "org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature");
    return props;
}

But even though the source code is implementation independent, the application is still dependent on the Jersey roles feature. If you decided you wanted to port, you would still need a replacement for the feature.

If you wanted to stay completely independent, you could implement the feature yourself. It's not all that complicated. You can check out the source code for RolesAllowedDynamicFeature. If you decide to try and implement the same, just annotate your implementation class with @Provider, and it should get picked up.