Jersey (and JAX-RS in general) allows simple dependency injection as follows:
#Path("/")
public class MyResource {
#Context private Application application;
...
}
Jersey first creates the class, then it binds dependencies to it. Is this a mechanism I can re-use for instances whose lifecycle I control entirely?
For example, consider an interface with some unknown implementations.
public interface MyInterface {
public boolean isHappy();
}
Suppose I have a list of these in one of my provider singletons, for example, an ExceptionMapper. It would be initialized in some subclass of Application.
#Provider
public class MyExceptionMapper implements ExceptionMapper<Exception> {
private List<MyInterface> list;
public ExceptionMapper(List<MyInterface> list) {
this.list = list;
}
#Override
public Response toResponse(Exception e) {
for (MyInterface item : list) {
// Manually bind dependencies here?
if (item.isHappy()) {
return Response.ok("Nope, no errors here. Promise.").build();
}
}
return Response.serverError().build();
}
}
And finally, suppose there's a specific implementation of this interface that needs access to the Application:
public class MyImplementation implements MyInterface {
#Context private Application application; // Can't do this
#Override
public boolean isHappy() {
MyApplication myApp = (MyApplication) application;
return myApp.shouldIgnoreExceptions(); // NullPointerException
}
}
Is there a way to bind contexts for all of the implementations of my interface? Or do I need to find a way to get Jersey to manage all implementations (by making them providers)? Or must I resort to CDI to perform dependency injection?
Note that I'm using Jersey 1.17.1, and I'd like to avoid defining any particular dependency as being part of the interface.
The JAX-RS way is to implement your own ContextResolver for MyInterface. Then you can manage the implementation of the contexts inside the context resolved. Context resolver is provider and is managed by Jersey, but not the MyInterface implementations.
Related
In a Spring application that uses HTTP remoting, I have a service façade module configured as follows (I made the code generic to improve clarity):
#Configuration
public class MyFacadeConfig {
private HttpInvokerServiceExporter facade(Class<?> cls) {
HttpInvokerServiceExporter bean = new HttpInvokerServiceExporter();
// The service referred to by this exporter is already instantiated as another Spring bean with all its dependencies.
bean.setService(appContext.getBean(cls));
bean.setServiceInterface(cls);
return bean;
}
#Bean("/first.service")
public HttpInvokerServiceExporter firstServiceFacade() {
return facade(FirstService.class);
}
#Bean("/second.service")
public HttpInvokerServiceExporter secondServiceFacade() {
return facade(SecondService.class);
}
// ... and so on for the 37 other services
}
where FirstService and SecondService are interfaces with existing implementations whose detail is not needed here.
I have another module that defines 39 proxies (instances of HttpInvokerProxyFactoryBean) corresponding to each of my services exposed through my façade.
So far, everything works properly.
But I would like to make the code more generic, elegant, and robust while mitigating the risk of error (e.g., a bad mapping between a service and its proxy in the future). The way I would like to do this is as follows:
First, I move the façade/proxy metadata into an enumeration:
public enum ConfigBeansFacade {
FIRST("/first", FirstService.class),
SECOND("/second", SecondService.class)
// ... and so on for the 37 other services
;
private String beanName;
private Class<?> serviceInterface;
// Constructor and getters
public String getCompleteBeanName() {
return beanName + ".service";
}
}
Then the configuration of the façade would be simplified in a style similar to the following:
#Configuration
public class MyFacadeConfig {
#Autowired
private ConfigurableBeanFactory beanFactory;
#Autowired
public void configExporters() {
for (ConfigBeansFacade bean : ConfigBeansFacade.values()) {
HttpInvokerServiceExporter exp = new HttpInvokerServiceExporter();
exp.setService(beanFactory.getBean(bean.getServiceInterface()));
exp.setServiceInterface(bean.getServiceInterface());
beanFactory.registerSingleton(bean.getCompleteBeanName(), exp);
}
}
}
I tried every single recipe I found in online forums, including StackOverflow, but there are two constraints not met elsewhere:
When defining the exporters, the underlying services are other Spring beans that are instantiated, initialized, and registered with their own configuration and dependencies through the standard Spring mechanics. There is no direct class instantiation other than the exporters themselves.
I thought about grouping the exporters into a single collection as suggested by some people. The only problem is that Spring MVC uses the HttpInvokerServiceExporter Spring bean names as endpoint URIs when registering the exporters into its own configuration. I must therefore register each exporter as a “first-class citizen” bean with its own bean name into the application context.
Given these constraints, the problem I have arises in (1) when I try to retrieve the underlying services to be encapsulated into exporters: they are not necessarily ready yet, which results into UnsatisfiedDependencyExceptions.
I tried solutions with a #PostContruct-annotated method, with a BeanPostProcessor, with an #Autowired method (as shown above), nothing is working as required.
Does anyone know about a way or a technique to initialize and register multiple beans inside a single method under my constraints described above? Such a method doesn't need to be annotated with #Bean, #Autowired, or any other specific annotation, it's just an example of what I tried.
In the client module, mercifully, the HttpInvokerProxyFactoryBean instances need only the interfaces and the bean names, so constraint (1) above should not apply.
Thanks in advance for any help you can provide...
I'm not 100% I've understood what you're trying to do but I wonder if you could try autowiring a List of beans that implement an interface?
e.g.
public interface MyService {
String getKey();
void doStuff();
}
Then implement as many of these as you require
e.g.
#Component
public class FirstService implements MyService {
public String getKey() {
return "/first";
}
public void doStuff() {
...
}
}
then have a factory bean with the autowired list
#Component
public class MyServiceFactory {
private final List<MyService> services;
#Autowired
public MyServiceFactory(List<MyService> services) {
this.services = services;
}
}
To add more implementations of MyService, simply add them as #Component and Spring magically picks them up and adds them to the list.
Sometimes I find it useful to access my implementations via a Map
#Component
public class MyServiceFactory {
private final Map<String, MyService> services;
#Autowired
public MyServiceFactory(List<MyService> services) {
this.services = services
.stream()
.collect(toMap(MyService::getKey, Function.identity()));
}
public MyService getServiceByKey(String key) {
return services.get(key);
}
}
I find this keeps each implementation nice and self contained (and easy to test). Spring automatically picks up all the components that implement my interface without the factory having a huge number of imports. And I can test the factory easily by mocking the list of implementations.
How can I get Jersey to inject classes without creating and registering factories on a one-for-one basis?
I have the following config:
public class MyConfig extends ResourceConfig {
public MyConfig() {
register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(FooFactory.class).to(Foo.class);
bindFactory(BazFactory.class).to(Baz.class);
}
});
}
}
hk2 will now successfully inject Foo and Baz:
// this works; Foo is created by the registered FooFactory and injected
#GET
#Path("test")
#Produces("application/json")
public Response getTest(#Context Foo foo) {
// code
}
But that's not my goal. My goal is to inject objects that wrap these classes. There are many and they each consume different combinations of Foo and Baz. Some examples:
public class FooExtender implements WrapperInterface {
public FooExtender(Foo foo) {
// code
}
}
public class FooBazExtender implements WrapperInterface {
public FooBazExtender(Foo foo, Baz baz) {
// code
}
}
public class TestExtender implements WrapperInterface {
public TestExtender(Foo foo) {
// code
}
// code
}
And so on.
The following does not work:
// this does not work
#GET
#Path("test")
#Produces("application/json")
public Response getTest(#Context TestExtender test) {
// code
}
I could create a factory for each and register it in my application config class, using the bindFactory syntax like I did with Foo and Baz. But that is not a good approach due to the number of objects in question.
I have read much of the hk2 documentation, and tried a variety of approaches. I just don't know enough of how hk2 actually works to come up with the answer, and it seems like a common enough problem that there should be a straightforward solution.
Factories are really only needed for more complex initializations. If you don't need this, all you need to do is bind the service
#Override
protected void configure() {
// bind service and advertise it as itself in a per lookup scope
bindAsContract(TestExtender.class);
// or bind service as a singleton
bindAsContract(TestExtender.class).in(Singleton.class);
// or bind the service and advertise as an interface
bind(TestExtender.class).to(ITestExtender.class);
// or bind the service and advertise as interface in a scope
bind(TestExtender.class).to(ITestExtender.class).in(RequestScoped.class);
}
You also need to add #Inject on the constructors so HK2 knows to inject the Foo and Baz
#Inject
public TestExtender(Foo foo, Baz baz) {}
I wound up using FastClasspathScanner to grab classes from the package(s) I was interested in. Then I called the appropriate bind methods (bindAsContract or bind) in batches, as mentioned in Paul Samsotha's answer (after also adding the appropriate #Inject annotations).
That seemed to be the most expedient method available to emulate autoscanning and avoid having to manually register each class.
It feels like a hack and I'd be surprised if hk2 doesn't have a better method baked in.
[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
The problem was actually with Jersey component registrations.
Even though I was registering binder instances, Jersey was checking the class (ServiceBinder) and discarding all but the first registration (WARN: existing registration found for the type).
This seems a bit bogus given I am registering instances, and I wish Jersey would fail with an error rather than log a warning when failing to register a component, but the solution is to simply change the registration pattern slightly:
// Doesn't work
register(new ServiceBinder<>(MyService1.class));
register(new ServiceBinder<>(MyService2.class));
// Works like a charm
register(new ServiceBinder(MyService1.class, MyService2.class));
where obviously the ServiceBinder is adjusted to call bindFactory for each supplied service.
#Path("/test")
public class MyClass {
#GET
public Response response() {
// Generating some expensive object here.
}
Right now I load the data into arrays etc inside the "response" function, but I want to do it before the query is even made. This way, I want to avoid reloading the data every time a a query is made. How do I achieve this?
This depends on your framework. Are you using the reference implementation Jersey? Jersey comes bundled with HK2 automatically. Then you could add
#Path("/test")
public class MyClass {
#Inject
private MyState stateful;
// here comes your code...
}
to your resource. Of course, you would still need to configure what to inject into this field. With HK2, you use an AbstractBinder for this purpose:
class MyBinder extends AbstractBinder {
private final MyState stateful;
public MyBinder (MyState stateful) {
this.stateful = stateful;
}
#Override
protected void configure() {
bind(stateful).to(MyState.class);
}
}
Finally, you need to add this binder on the application's setup. For this purpose, JAX-RS Application object can be queried for singletons. Simply add the required instance to the application such that it is returned by Application#getSingletons as here:
class MyJaxRSApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
return Collections.singletonSet(MyClass.class);
}
#Override
public Set<Object> getSingletons() {
return Collections.singletonSet(new MyBinder(new MyStateImpl()));
}
}
You can now run your application where MyStateImpl is always injected into MyClass.
I want expose instances managed by an external framework to CDI applications using #Inject. These instances must be provided this other framework since their lifecycle is based on various caching strategies.
Ex: same instance is visible within same thread scope, might live across many request scopes, session scope is not applicable. Seems I need to define a new scope targeting these kind of instances?
What is the best way to do this? An extension, is it possible with producer methods?
I almost got it to work with producer methods using the following:
#Inject
#CustomInject
FwObject obj;
#Produces
#CustomInject
FwObject createConfig(InjectionPoint p) {
return (FwObject) ctx.get((Class<?>) p.getType());
}
But this force me to be explicit about the type produced which is not possible since there is no common framework interface.
Any help appreciated.
Maybe with producer methods, all depends on what you need, but an extension is probably the best way to go. If you need to go with a new scope (if you're using JSF the Conversation scope may work) you will certainly need to create an extension.
I think I solved it by creating a custom scope. The following article was really helpful:
http://www.verborgh.be/articles/2010/01/06/porting-the-viewscoped-jsf-annotation-to-cdi/
This is a very brief description of how I solved it.
Create custom scope annotation.
import javax.enterprise.context.NormalScope;
#Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE, ElementType.METHOD })
#NormalScope
public #interface CustomScope {
}
Create custom context.
import javax.enterprise.context.spi.Context;
public class CustomContext implements Context {
private MyFw myFw = .... ;
#Override
public Class<? extends Annotation> getScope() {
return CustomScope.class;
}
#Override
public <T> T get(Contextual<T> contextual, CreationalContext<T> creationalContext) {
Bean bean = (Bean) contextual;
return (T) myFw.get(bean.getBeanClass());
}
#Override
public <T> T get(Contextual<T> contextual) {
Bean bean = (Bean) contextual;
return (T) myFw.get(bean.getBeanClass());
}
#Override
public boolean isActive() {
return true;
}
}
Create extension and register context.
import javax.enterprise.inject.spi.Extension;
public class CustomContextExtension implements Extension {
public void afterBeanDiscovery(#Observes AfterBeanDiscovery event, BeanManager manager) {
event.addContext(new CustomContext());
}
}
Register extension.
Add CustomContextExtension to META-INF/javax.enterprise.inject.spi.Extension
Add CustomScope to framework object.
#CustomScope
public class FwObject { ... }
Inject FwObject using #Inject where needed.
public class MyService {
#Inject
FwObject obj;
}