I am using Jersey 1.17 and Jersey-Spring 1.8.
I am trying to get a jersey ResourceFilter to install correctly. I want this filter to be application-wide.
I have defined the ResourceFilter implementation as a bean in spring (defined in XML) and annotated it with the #Provider annotation.
#Provider
public class ContainerResourceFilterTest implements ResourceFilter
{
public ContainerRequestFilter getRequestFilter()
{
return null; //TODO
}
public ContainerResponseFilter getResponseFilter()
{
return null; //TODO
}
}
But it doesn't get hit when I send a request in.
If I use the #ResourceFilters annotation on a particular resource then it works as expected, but I don't want to do that on every single class.
How do I register a filter that is application wide?
Answering my own questions.
Doing some further reading of the jersey source code I have found that the #Provider annotation doesn't do anything for ResourceFilterFactory or ContainerRequest/Response. These can only be registered in one of the following 2 ways
Using the META-INF/services
Using the init params of the servlet, for example in web.xml
<init-param>
<param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
<param-value>com.my.path.ResourceFilterFactory</param-value>
</init-param>
Not sure why the code doesn't inspect the registered spring beans for #Providers that implement the appropriate interfaces.
Worth noting that registering a class that implements ResourceFilter does not work for either of the above methods. You must do it for ResourceFilterFactory and have that return ResourceFilter implementations.
The good news is once you have made jersey aware of the classes that need to be registered it calls into spring to provide those classes so auto-wiring etc works as per usual.
Jersey doesn't scan the classpath for everything - ResourceFilters are one of the things it won't find automagickally. If you're using a ResourceConfig class, register your ResourceFilter class:
#ApplicationPath("/myapp")
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages(getClass().getPackage().toString());
register(ContainerResourceFilterTest.class);
}
}
Jersey will then scan that class for the filter interface, create an instance (all filters are singleton scope according to their docs) and insert it into the Jersey filter chain.
Related
With JAXRS-2.0 (Jersey 2.2, specifically) I'm trying to apply a request interceptor to a specific resource provider class (which is in a 3rd party library), and I'm obviously doing it wrong. I am getting the error below - I'm a bit baffled as to the cause. The net effect is that the interceptor is being invoked on every request to every provider instead of the 1 provider. This is the error:
2017-11-26 10:43:51.061
[localhost-startStop-1][WARN][o.g.j.server.model.ResourceMethodConfig]
- The given contract (interface javax.ws.rs.container.DynamicFeature) of class com.idfconnect.XYZ provider cannot be bound to a resource
method.
The interceptor class is defined as:
#Provider
public class XYZ implements WriterInterceptor, DynamicFeature {
In my ResourceConfig I'm registering the interceptor for the specific provider as follows (I suspect this is where I've gone astray):
#ApplicationPath("service")
public class MyApp extends ResourceConfig {
public MyApp() {
ResourceConfig rc = register(SomeThirdPartyResource.class);
rc.register(XYZ.class);
...
Can someone help me figure out how to bind the interceptor to SomeThirdPartyResource class only?
You shouldn't make your provider implement DynamicFeature. This is probably the cause of the warning. You are trying to register the interceptor, which is also a DynamicFeature, and Jersey is telling you that DynamicFeature is not something that is supposed to be registered to a method.
You should make a separate class for the DynamicFeature and inside the configure check for the resource you want to attach your provider to (using the ResourceInfo, then register it accordingly. For example
class XYZ implements DynamicFeature {
#Override
public void configure(ResourceInfo info, FeatureContext ctx) {
if (info.getResourceClass().equals(ThirdPartyResource.class) {
ctx.register(YourWriterImplementation.class);
// or
ctx.register(new YourWriterImplementation());
}
}
}
The reason you are getting all the resources hit by the interceptor is because you are registering the interceptor with the ResourceConfig. This will attach it all resources. You only want to register the DynamicFeature and let it determine which resource to tie to.
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:
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)
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?
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:
When to Use JAX-RS Class-path Scanning Mechanism
Sevlet Based Deployment - Servlet 3.x Container
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.
i'm trying to create a global ExceptionHandler in this way:
#Provider
public class MyExceptionHandler implements ExceptionMapper<Exception>
{
#Override
public Response toResponse(Exception exception)
{
return Response.status(Status.BAD_REQUEST).entity("TEST").build();
}
}
in order to catch all the exception in the controller:
#Path("/policy")
#GET
public void getPolicy(#DefaultValue("no") #QueryParam("_id") String _id) throws Exception{
int a = Integer.parseInt("test"); // it generates NumberFormatException
}
the NumberFormatException isn't handled by MyExceptionHandler.
Where i'm wrong?
Thanks!
If you use jersey < 2.5 it could be a bug while component package scanning:
#see: https://java.net/jira/browse/JERSEY-2175
Otherwise, you properly missed to add the package of your MyExceptionHandler for component scans. The #Provider annotation means, that the implementation "should be discoverable by JAX-RS runtime during a provider scanning phase", but in fact it does'nt did it like we thought. Probably a bug, but i'm not sure.
Meaning: Afaig, you have to register package or mapper by yourself - otherwise it will not work for now!
Note: I did not tested this with servlet 3.x without Application subclass and basic web.xml #see 4.7.2.3.1. JAX-RS application without an Application subclass link
Maybe also interesting:
Disable MBW, MBR, ExceptionMapper automatic registration via META-INF/services
The jersey-metainf-services (Jersey extension module enabling automatic registration of JAX-RS providers (MBW/MBR/EM) via META-INF/services mechanism) was added #since 2.9.x
For the sake of completeness:
You can do the registration by using ResourceConfig.packages(String...packages):
import org.glassfish.jersey.server.ResourceConfig;
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
packages(new String[] {
"path.to.foo",
"path.to.bar"
});
}
}
Alternative in web.xml servlet config
...for packages:
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>path.to.foo,path.to.bar</param-value>
</init-param>
...for classes
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
path.to.foo.MyExceptionHandler,
path.to.bar.FooBar
</param-value>
</init-param>
It's worth checking that the ExceptionMapper is registered as a singleton. Registering a component as a singleton depends on your framework. Here's a couple of examples:
Guice
In your injector instance --
Guice.createInjector(new JerseyServletModule() {
#Override
protected void configureServlets() {
/* ... this is where modules are installed and where
* component dependencies are binded
*/
bind(MyExceptionHandler.class).in(Scopes.SINGLETON);
/* configure filters etc, omitted for brevity */
}
}
Spring
In your Spring config
<bean id="exceptionMapper" class="my.package.MyExceptionHandler" scope="singleton" />
Or using component scanning, mark your ExceptionMapper as #Component --
Config:
<context:component-scan base-package="my.package"/>
Annotated class:
#Component
#Provider
public class MyExceptionHandler implements ExceptionMapper<Exception> {
}
I have an ExceptionMapper defined as following
#Provider
public class MyExceptionMapper implements ExceptionMapper<Throwable> {
#Inject
private Manager myManager;
#Override
public Response toResponse(Throwable exception) {
// My implementation
}
}
Deploying this code on glassfish 4 results with exception:
org.glassfish.hk2.api.UnsatisfiedDependencyException:
There was no object available for injection at
Injectee(requiredType=Manager,parent=MyExceptionMapper,qualifiers {}),position=-1,optional=false,self=false,unqualified=null,955636053)
When I use #Context instead of #Inject I do not see the exception but myManager is null
I tried making MyManager as #ManagedBean, #Singleton or an EJB (Stateless, Singleton) and non works
In JEE6 (with glassfish 3) you have to add
#javax.annotation.ManagedBean
to the provider implementation. Possibly this works also for Glassfish 4
As far as I know, this issues comes from the following. CDI is not in place to manage the dependencies of restful services and providers by default. But when adding #ManagedBean you enable CDI to create the instance.
Here is an example where I introduced CDI to a restful service using jersey.
You can follow the updates regarding this issue here https://java.net/jira/browse/JERSEY-2393
You could use OmniFaces Beans to get a CDI managed bean instance in your ExceptionMapper:
Beans.getInstance(Bean.class)
I'm using this with #javax.ejb.Stateless beans containing #javax.persistence.PersistenceContext.
I've been searching everywhere, but can't seem to find a clear answer...
What is the mechanism whereby a server (glassfish for my problem) injects actual objets that are annotated with #Context? More specifically, if I wanted to write a class that did something like:
#Path("/")
public class MyResource {
#GET
public String doSomething(#Context MyObject obj) {
// ...
}
}
then how would I do it? Where is it that the MyObject is instanciated, who does it, and how?
Edit: I've seen stuff like the following:
Using #Context, #Provider and ContextResolver in JAX-RS
http://jersey.576304.n2.nabble.com/ContextResolver-confusion-td5654154.html
However, this doesn't square with what I've seen, e.g. in the constructor of org.neo4j.server.rest.web.RestfulGraphDatabase, which has the following signature:
public RestfulGraphDatabase(
#Context UriInfo uriInfo,
#Context Database database,
#Context InputFormat input,
#Context OutputFormat output,
#Context LeaseManager leaseManager )
You can write your own injection provider and plug that into Jersey - look at SingletonTypeInjectableProvider and PerRequestTypeInjectableProvider - extend one of these classes (depending on the lifecycle you want for the injectable object) and register your implementation as a provider in your web app.
For example, something like this:
#Provider
public class MyObjectProvider extends SingletonTypeInjectableProvider<Context, MyObject> {
public MyObjectProvider() {
// binds MyObject.class to a single MyObject instance
// i.e. the instance of MyObject created bellow will be injected if you use
// #Context MyObject myObject
super(MyObject.class, new MyObject());
}
}
To include the provider in your web app you have several options:
if your app uses classpath scanning (or package scanning) just make sure the provider is in the right package / on the classpath
or you can simply register it using META-INF/services entry (add META-INF/services/com.sun.jersey.spi.inject.InjectableProvider file having the name of your provider class in it's contents)
I think I may be on to something...and if this works, Martin should get partial credit. :)
It appears that the #Provider class must implement the com.sun.jersey.spi.inject.Injectable<T> interface. However, I'm not sure that this is enough to actually have the #Context be injected. What's missing, is that we have to tell the ResourceConfig object of the web app about the #Provider. In the context of what I'm trying to do, and taking hints from neo4j-server, the remaining work boils down to:
extending com.sun.jersey.spi.container.servlet.ServletContainer, and overriding the configure method:
#Override
protected void configure(WebConfig wc, ResourceConfig rc, WebApplication wa)
{
super.configure( wc, rc, wa );
Set<Object> singletons = rc.getSingletons();
singletons.add(new MyObjectProvider());
}
specifying that this container must be used in the web.xml deployment descriptor:
<servlet>
<servlet-name>JAX-RS Servlet Container</servlet-name>
<servlet-class>com.blah.MyServletContainer</servlet-class>
</servlet>
I don't think you can use #Context with a user-defined type like MyObject. It is for injecting types that jax-ws already understands. It is mentioned here.
Chapter 5 of the JAX-RS specification presents all the standard JAX-RS Java types that may be used with #Context.
You probably want to use something like #FormParam or #PathParam instead. See section 2.3 of the spec for a description. Here is your answer, copied from that section of the spec:
In general the Java type of the method parameter may:
Be a primitive type;
Have a constructor that accepts a single String argument;
Have a static method named valueOf or fromString that accepts a single String argument (see, for example, Integer.valueOf(String) and java.util.UUID.fromString(String)); or
Be List, Set or SortedSet, where T satisfies 2 or 3 above. The resulting collection is read-only.
See chapters 5-6 of the JAX-RS spec. That should tell you everything you need to know about it.