Reputation: 11662
In Jersey 2, can I inject a custom, request-specific value into my resource? Specifically, I would like to inject a MyThing
which can be derived from my custom security context MySecurityContext
. I would like to inject MyThing
directly to make the code clean.
Is there any way to do this? According to this question it can't be done using a ContextResolver
although this article and this example suggest it might be possible.
Using an auth filter, I am able to set my custom security context using code like this:
@Provider
public class HttpTokenAuthFilter implements IComposableJaxRsAuthFilter {
@Override
public boolean doAuth(ContainerRequestContext requestContext) throws WebApplicationException {
// error handling omitted
requestContext.setSecurityContext(MySecurityContext.fromHeaders(requestContext));
}
}
... and then in my resource I can pull a value from it:
@Path("/foo")
public class MyResource {
@Context
private SecurityContext securityContext;
@Get
public String doGetFoo() {
MyThing myThing = ((MySecurityContext)securityContext).getThing();
// use myThing to produce a result
}
... however, since this is going to be repeated a lot, I would much rather just write:
@Context
private MyThing myThing;
I tried defining a ContextResolver
. I see it getting constructed, but I never see it getting invoked, so I have not yet tried any of the techniques linked above. Is this even the correct class to be using?
@Provider
public class MyThingResolver implements ContextResolver<MyThing> {
public MyThingResolver() {
System.out.println("ctor");
}
@Override
public MyThing getContext(Class type) {
System.out.println("getContext");
if (type.equals(MyThing.class)) {
return new MyThing(); // TODO: SHOULD ACTUALLY USE CURRENT MySession
}
return null;
}
}
Upvotes: 3
Views: 1858
Reputation: 11662
Per this answer and the refinements specified at this followup, it's almost possible to accomplish the injection using a Factory
. The only caveat is, you must inject MyThing
via a Provider
, otherwise it's going to get created (with the default SecurityContext
) before the filter runs and swaps in the MySecurityContext
.
The factory code looks like this:
public class MyThingFactory implements Factory<MyThing> {
@Context
private SecurityContext securityContext;
@Override
public MyThing provide() {
return ((MySecurityContext)securityContext).getThing();
}
@Override
public void dispose(MyThing session) {
}
}
The resource can then inject it like this:
@Context
private Provider<MyThing> myThingProvider;
... and consume it like this:
MyThing myThing = myThingProvider.get();
// use myThing
The factory registration in the AbstractBinder
looks like this:
this.bindFactory(MyThingFactory.class) //
.to(MyThing.class) //
.in(RequestScoped.class);
Per the comment from @peeskillet, it is possible to get rid of the Provider
by proxying MyThing
. (Per @ jwells131313, MyThing
must therefore be an interface or a proxy-able class.)
The binding then looks like this:
this.bindFactory(MyThingFactory.class) //
.to(MyThing.class) //
.proxy(true) //
.in(RequestScoped.class);
and injection finally works as desired:
@Context
private MyThing myThing;
Upvotes: 5