3

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?

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
gb.
  • 168
  • 3
  • 11

1 Answers1

4

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.

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • Thanks for the excellent answer. After researching previously, I actually leaned more towards implementing the feature myself and did check the source for `RolesAllowedDynamicFeature` but didn't know if this was a good idea. At least I wanted to know if the community knew of a better way. Another question, are you aware of any issues with configuring the implementation-specific servlet in the web.xml (even for 3.0 containers) and probably set the package scanning property there? This way if I needed to switch to another impl, I could just change the web.xml config? – gb. Nov 12 '15 at 04:34
  • Jersey offers web.xml config where you can configure the package scanning. But AFAIK, Jersey is the only implementation with a package scanning feature. So theoretically, you _could_ take advantage of the package scanning just for Jersey, and with other impls, just make some slight changes to configuration to go back to standard behavior. I have not tired this, that's what I say _theoretically_. I'm pretty sure it's not too difficult, the the tricky part it to think about what is the best configuration combination that would cause the least pain in a port. – Paul Samsotha Nov 12 '15 at 04:50
  • Thanks again for the insight. I feel a little better off to make a decision. – gb. Nov 12 '15 at 05:08
  • After reading your question again, note that RESTeasy also has a [roles based feature](https://github.com/resteasy/Resteasy/tree/master/jaxrs/resteasy-jaxrs/src/main/java/org/jboss/resteasy/plugins/interceptors) also using the annotations. So you might be able to configure both in your app using simple String configurations (no implementation dependencies). See [RESTeasy switches](http://docs.jboss.org/resteasy/docs/3.0.12.Final/userguide/html/Installation_Configuration.html#d4e127). I don't know the behavior of combining the class-path scanning with the web.xml configuration of providers... – Paul Samsotha Nov 12 '15 at 05:22
  • ...but you can test it out. For instance, use the `getProperties()` (for Jersey, and you can keep if for RESTeasy also - has no effect) and also register the RESTeasy role feature in a context-param in the web.xml. This should have no affect on Jersey. Now this is all theoretical. I have not tried this either, but it is another option you can test out. – Paul Samsotha Nov 12 '15 at 05:24
  • Personally though, if I was tasked with keeping the application portable, I would just implement it myself :-) – Paul Samsotha Nov 12 '15 at 05:56
  • That was my first choice but then I thought, what if there are other impl-specific providers that I need later on? (not feasible/maintainable to implement them all) So right now, keeping everything in the web.xml works well enough. Truth is, I'm thinking that in my specific case, where I'm in control of deployment, trying to keep the app portable doesn't seem worth the effort. It would probably make more sense to just update the Application class with the required providers any time we are changing implementations. Seems like JAX-RS could use a bit of an enhancement in this area. – gb. Nov 12 '15 at 14:53