[EDIT] The problem is with the register(new ServiceBinder<>(MyService.class)); Jersey generates a warning and ignores the registration for all but the first one (Existing previous registration found for the type); it only considers the type-erased ServiceBinder class to decide there is a conflict. It looks like I need to use a more sophisticated version of register to get past that issue. [/EDIT]
In Jersey 1 I was able to use custom injectable providers to inject my objects into both class fields and method parameters, by extending LazySingletonInjectableProvider
I can't figure out how to port that pattern to Jersey 2 (with hk2 on Tomcat 7). I have read everything I could find on the topic, including Jersey custom method parameter injection with inbuild injection - but I don't want to use a custom annotation, and I am not trying to inject a request parameter.
[EDIT] I made the wrong assumption regarding what works and what doesn't:
- Injection into a class field in a ContainerRequestFilter works fine
- Injection into a resource, either as class field or method parameter does not work
[EDIT 2]: The InjectionResolver as described below actually doesn't work at all, I have removed it. Jersey already has a ContextInjectionResolver which presumably should take care of the @Context annotation.
I have created and registered an AbstractBinder, and with that class field injection works fine; however method parameter injection doesn't (the binder never gets invoked and the parameter remains null).
I have tried to bind an InjectionResolver but that didn't help either.
Any suggestion on how to make this work would be greatly appreciated... here is the current code:
The HK2 binder:
public class ServiceBinder<T> extends AbstractBinder
{
private final Factory<T> _factory;
private final Class<? extends T> _clazz;
public OsgiServiceBinder(Class<T> clazz)
{
_factory = new ServiceFactory<>(clazz);
_clazz = clazz;
}
protected void configure()
{
bindFactory(_factory).to(_clazz); //.in(RequestScoped.class);
bind(ServiceInjectionResolver.class)
.to(new TypeLiteral<InjectionResolver<Context>>() { })
.in(PerLookup.class);
}
}
The injection resolver:
public class ServiceInjectionResolver<T> implements InjectionResolver<Context>
{
private Class<T> _clazz;
public OsgiServiceInjectionResolver(Class<T> clazz)
{
_clazz = clazz;
}
public Object resolve(Injectee injectee, ServiceHandle<?> root)
{
if (_clazz.getCanonicalName().equals(injectee.getRequiredType().getTypeName())) {
return Framework.getService(_clazz);
}
return null;
}
public boolean isConstructorParameterIndicator()
{
return false;
}
public boolean isMethodParameterIndicator()
{
return true;
}
}
The JAX-RS registration:
public class MyApplication extends Application
{
public MyApplication()
{
registerClasses(<resource classes>);
register(new ServiceBinder<>(MyService.class));
}
}
The resource class:
@Path("/schedules")
public class SchedulesResource
{
@Context UriInfo _uriInfo;
// This injection works fine, _service1 is properly initialized
@Context MyService _service1;
@PUT
@Consumes({MediaType.APPLICATION_JSON})
@Path("{jobGroup}/{jobName}")
public Response putSchedule(@Context MyService service2,
...)
{
// The injection of service2 doesn't work...
}
}
The Factory class:
public class ServiceFactory<T> implements Factory<T>
{
private Class<T> _clazz;
protected ServiceFactory(Class<T> clazz)
{
_clazz = clazz;
}
public T provide()
{
return Framework.getService(_clazz);
}
}
public void dispose(T t)
{
}
}
pok