Inject not working for nested objects[Jersey 2.22.1] - java

I have a Jersey resource with a facade object injected. This is configured in my ResourceConfig and the facade gets injected fine. The facade contains a DAO class which also should be injected and is configured in the same ResourceConfig. Now to my problem; the DAO class is null. Thus, not injected.
#ApplicationPath("/service")
public class SystemSetup extends ResourceConfig {
public SystemSetup() {
packages(false, "com.foo.bar");
packages("org.glassfish.jersey.jackson");
register(JacksonFeature.class);
final LockManager manager = getLockManager();
final SessionFactory sessionFactory = getSessionFactory();
register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(InjectFactory.getDaoFactory(sessionFactory)).to(Dao.class).in(Singleton.class);
bindFactory(InjectFactory.getFacadeFactory(manager)).to(Facade.class).in(Singleton.class);
}
});
}
#Path("/")
#Produces("text/json")
public class ViewResource {
#Inject
private Facade logic;
public class Facade {
#Inject
private Dao dao; //Not injected
The factory instances are rather simple. They simply call the constructor and pass the argument to it.
The strange thing is that this worked absolut fine when I used bind(Class object) rather than bindFactory.
EDIT
Factories
class InjectFactory {
static Factory<Dao> getDaoFactory() {
return new Factory<Dao>() {
#Override
public Dao provide() {
return new Dao(new Object());
}
#Override
public void dispose(Dao dao) {}
};
}
static Factory<Facade> getFacadeFactory() {
return new Factory<Facade>() {
#Override
public Facade provide() {
return new Facade();
}
#Override
public void dispose(Facade facade) {}
};
}
}

As is the case with most Di frameworks, when you start instantiating things yourself, it's often the case that you are kicking the framework out of the equation. This holds true for the Factory instances, as well as the objects the factory creates. So the Facade instance never gets touch by the framework, except to inject it into the resource class.
You can can a hold of the ServiceLocator, and explicitly inject objects yourself if you want to create them yourself. Here are a couple options.
1) Inject the ServiceLocator into the Factory instance, then inject the Facade instance.
static Factory<Facade> getFacadeFactory() {
return new Factory<Facade>() {
#Context
ServiceLocator locator;
#Override
public Facade provide() {
Facade facade = new Facade();
locator.inject(facade);
return facade;
}
#Override
public void dispose(Facade facade) {}
};
}
#Inject
public SystemSetup(ServiceLocator locator) {
packages("foo.bar.rest");
packages("org.glassfish.jersey.jackson");
register(JacksonFeature.class);
register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(InjectFactory.getDaoFactory()).to(Dao.class);
Factory<Facade> factory = InjectFactory.getFacadeFactory();
locator.inject(factory);
bindFactory(factory).to(Facade.class);
}
});
}
2) Or bind a Factory class, and let the framework inject the ServiceLocator
public static class FacadeFactory implements Factory<Facade> {
#Context
ServiceLocator locator;
#Override
public Facade provide() {
Facade facade = new Facade();
locator.inject(facade);
return facade;
}
#Override
public void dispose(Facade facade) {}
}
register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(InjectFactory.getDaoFactory()).to(Dao.class);
bindFactory(InjectFactory.FacadeFactory.class).to(Facade.class);
}
});

Related

Jersey Test + HK2 + Inject Factory service

I am trying to inject a object provided by HK2 Factory service in a Jersey Test Class but getting unsatisfied dependencies exception.
I have a factory service as below
public class TestFactory implements Factory<TestObject>{
private final CloseableService closeService;
#Inject
public TestFactory(CloseableService closeService) {
this.closeService = closeService;
}
#Override
public TestObject provide() {
TestObject casualObject = new TestObject();
this.closeService.add(() -> dispose(casualObject));
return casualObject;
}
#Override
public void dispose(TestObject instance) {
instance.destroy();
}
}
And a Jersey Test class
public class SampleTestCass extends JerseyTestNg.ContainerPerClassTest
{
//#Inject
private TestObject myTestObject;
private ServiceLocator locator;
#Override
protected Application configure()
{
ResourceConfig resConfig = new ResourceConfig(MyApi.class);
resConfig.register(getBinder());
locator = setupHK2(getBinder());
return resConfig;
}
// setup local hk2
public setupHK2(AbstractBinder binder)
{
ServiceLocatorFactory factory = ServiceLocatorFactory.getInstance();
ServiceLocator locator = factory.create("test-locator");
DynamicConfigurationService dcs = locator.getService(DynamicConfigurationService.class);
DynamicConfiguration dc = dcs.createDynamicConfiguration();
locator.inject(binder);
binder.bind(dc);
dc.commit();
return locator;
}
// get the binder
public AbstractBinder getBinder()
{
return new AbstractBinder() {
#Override
protected void configure() {
bindFactory(TestFactory.class, Singleton.class).to(TestObject.class).in(PerLookup.class);
}
}
}
#BeforeClass
public void beforeClass()
{
myTestObject = locator.getService(TestObject.class);
// use myTestObject
}
#AfterClass
public void afterClass()
{
if (locator != null) {
locator.shutdown();
}
}
#Test()
public void someTest()
{
// some test code...
}
}
And getting below exceptions
A MultiException has 3 exceptions. They are:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=CloseableService,parent=TestFactory,qualifiers={},position=0,optional=false,self=false,unqualified=null,2053349061)
java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.test.factories.TestFactory errors were found
java.lang.IllegalStateException: Unable to perform operation: resolve on com.test.factories.TestFactory
CloseableService is a service available within a Jersey application. The ServiceLocator you created is not tied to the Jersey application. It is just a standalone locator. So trying to register the TestFactory with this locator will cause it to fail, as there is no CloseableService. The one that you registered with the ResourceConfig will work just fine.
Not sure what exactly you're trying to do, but if you want access to the service inside the test, one thing you can do is just bind the service as an instance, something like
class MyTest {
private Service service;
#Override
public ResourceConfig configure() {
service = new Service();
return new ResourceConfig()
.register(new AbstractBinder() {
#Override
public void configure() {
bind(service).to(Service.class);
}
})
}
}

Create #UnitOfWork-aware proxies of existing objects

I have the following classes:
public FooDAO extends AbstractDAO<Foo> { // Dropwizard DAO
#Inject FooDAO(SessionFactory sf) { super(sf); }
public void foo() { /* use SessionFactory */ }
}
public class FooService {
private final FooDAO fooDAO; // Constructor-injected dependency
#Inject FooService (FooDAO fooDAO) { this.fooDAO = fooDAO; }
#UnitOfWork
public void foo() {
this.fooDAO.foo();
System.out.println("I went through FooService.foo()");
}
}
Now, FooService is not a resource, so Dropwizard doesn't know about it and doesn't automagically proxy it. However the smart guys at Dropwizard made it so I can get a proxy through UnitOfWorkAwareProxyFactory.
I tried doing feeding these proxies to Guice with an interceptor, but I faced an issue because UnitOfWorkAwareProxyFactory only ever creates new instances and never lets me pass existing objects. The thing with new instances is that I don't know the parameters to give it since they're injected by Guice.
How do I create #UnitOfWork-aware proxies of existing objects?
Here's the interceptor I've made so far:
public class UnitOfWorkModule extends AbstractModule {
#Override protected void configure() {
UnitOfWorkInterceptor interceptor = new UnitOfWorkInterceptor();
bindInterceptor(Matchers.any(), Matchers.annotatedWith(UnitOfWork.class), interceptor);
requestInjection(interceptor);
}
private static class UnitOfWorkInterceptor implements MethodInterceptor {
#Inject UnitOfWorkAwareProxyFactory proxyFactory;
Map<Object, Object> proxies = new IdentityHashMap<>();
#Override public Object invoke(MethodInvocation mi) throws Throwable {
Object target = proxies.computeIfAbsent(mi.getThis(), x -> createProxy(mi));
Method method = mi.getMethod();
Object[] arguments = mi.getArguments();
return method.invoke(target, arguments);
}
Object createProxy(MethodInvocation mi) {
// here, what to do? proxyFactory will only provide objects where I pass constructor arguments, but... I don't have those!
}
}
}
Of course, if Dropwizard (or Guice) offers me a simpler way to do so, which is it?
As from Dropwizard 1.1: (not yet released, as of August 10, 2016)
public class UnitOfWorkModule extends AbstractModule {
#Override
protected void configure() {
UnitOfWorkInterceptor interceptor = new UnitOfWorkInterceptor();
bindInterceptor(Matchers.any(), Matchers.annotatedWith(UnitOfWork.class), interceptor);
requestInjection(interceptor);
}
#Provides
#Singleton
UnitOfWorkAwareProxyFactory provideUnitOfWorkAwareProxyFactory(HibernateBundle<AlexandriaConfiguration> hibernateBundle) {
return new UnitOfWorkAwareProxyFactory(hibernateBundle);
}
private static class UnitOfWorkInterceptor implements MethodInterceptor {
#Inject
UnitOfWorkAwareProxyFactory proxyFactory;
#Override
public Object invoke(MethodInvocation mi) throws Throwable {
UnitOfWorkAspect aspect = proxyFactory.newAspect();
try {
aspect.beforeStart(mi.getMethod().getAnnotation(UnitOfWork.class));
Object result = mi.proceed();
aspect.afterEnd();
return result;
} catch (InvocationTargetException e) {
aspect.onError();
throw e.getCause();
} catch (Exception e) {
aspect.onError();
throw e;
} finally {
aspect.onFinish();
}
}
}
}

Java Deep Injection with custom annotations preferably with hk2 [duplicate]

I have a Jersey resource with a facade object injected. This is configured in my ResourceConfig and the facade gets injected fine. The facade contains a DAO class which also should be injected and is configured in the same ResourceConfig. Now to my problem; the DAO class is null. Thus, not injected.
#ApplicationPath("/service")
public class SystemSetup extends ResourceConfig {
public SystemSetup() {
packages(false, "com.foo.bar");
packages("org.glassfish.jersey.jackson");
register(JacksonFeature.class);
final LockManager manager = getLockManager();
final SessionFactory sessionFactory = getSessionFactory();
register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(InjectFactory.getDaoFactory(sessionFactory)).to(Dao.class).in(Singleton.class);
bindFactory(InjectFactory.getFacadeFactory(manager)).to(Facade.class).in(Singleton.class);
}
});
}
#Path("/")
#Produces("text/json")
public class ViewResource {
#Inject
private Facade logic;
public class Facade {
#Inject
private Dao dao; //Not injected
The factory instances are rather simple. They simply call the constructor and pass the argument to it.
The strange thing is that this worked absolut fine when I used bind(Class object) rather than bindFactory.
EDIT
Factories
class InjectFactory {
static Factory<Dao> getDaoFactory() {
return new Factory<Dao>() {
#Override
public Dao provide() {
return new Dao(new Object());
}
#Override
public void dispose(Dao dao) {}
};
}
static Factory<Facade> getFacadeFactory() {
return new Factory<Facade>() {
#Override
public Facade provide() {
return new Facade();
}
#Override
public void dispose(Facade facade) {}
};
}
}
As is the case with most Di frameworks, when you start instantiating things yourself, it's often the case that you are kicking the framework out of the equation. This holds true for the Factory instances, as well as the objects the factory creates. So the Facade instance never gets touch by the framework, except to inject it into the resource class.
You can can a hold of the ServiceLocator, and explicitly inject objects yourself if you want to create them yourself. Here are a couple options.
1) Inject the ServiceLocator into the Factory instance, then inject the Facade instance.
static Factory<Facade> getFacadeFactory() {
return new Factory<Facade>() {
#Context
ServiceLocator locator;
#Override
public Facade provide() {
Facade facade = new Facade();
locator.inject(facade);
return facade;
}
#Override
public void dispose(Facade facade) {}
};
}
#Inject
public SystemSetup(ServiceLocator locator) {
packages("foo.bar.rest");
packages("org.glassfish.jersey.jackson");
register(JacksonFeature.class);
register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(InjectFactory.getDaoFactory()).to(Dao.class);
Factory<Facade> factory = InjectFactory.getFacadeFactory();
locator.inject(factory);
bindFactory(factory).to(Facade.class);
}
});
}
2) Or bind a Factory class, and let the framework inject the ServiceLocator
public static class FacadeFactory implements Factory<Facade> {
#Context
ServiceLocator locator;
#Override
public Facade provide() {
Facade facade = new Facade();
locator.inject(facade);
return facade;
}
#Override
public void dispose(Facade facade) {}
}
register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(InjectFactory.getDaoFactory()).to(Dao.class);
bindFactory(InjectFactory.FacadeFactory.class).to(Facade.class);
}
});

Do I need to use a factory in order to inject a simple object in Jersey?

As of right now in order to inject an object of type Foo into Bar I do the following.
Class to be injected into:
class Bar {
#Inject
Foo field;
public Foo getField() { return field; }
}
Code implementing Factory:
public class Foo implements Factory<Foo> {
#Override
public Foo provide() {
return new Foo();
}
#Override
public void dispose(Foo f) {
}
}
Code in Main Configuration:
final ResourceConfig rc = new ResourceConfig()
.packages("com.example")
.register(
new AbstractBinder() {
#Override
protected void configure() {
bindFactory(new FooFactory()).to(Foo.class).in(Singleton.class);
}
})
My question is... In Jersey 2.0 which uses hk2 for dependency injection is there anyway to inject an object without creating a factory class?
It is possible to use an AbstractBinder for injection binding without a Factory or an InjectionResolver.
register(new AbstractBinder() {
#Override
protected void configure() {
bind(Foo.class).to(Foo.class);
bind(Bar.class).to(Bar.class);
}
});
In this case the classes Foo and Bar have empty constructors.
public class Bar {
#Inject
Foo field;
public Foo getField() { return field; }
}
public class Foo {}
Now you can inject Bar into a resource or other hk2 beans.

Override jersey resource with guice

I'm looking for a method to override jersey resources bounded with guice in GuiceServletContextListener. My code, which I'm trying to get working:
//Define Jersey resource interface
#Path("/books/{key}")
public interface BookDocument {
public BookDAO getDao();
public void setDao(BookDAO dao);
}
//Define default implementation
public class BookImpl implements Book {
#Override
public BookDAO getDao() {
return dao;
}
#Inject
#Override
public void setDao(BookDAO dao) {
this.dao = dao;
}
}
//User wants to inject his implementation, so he define it
public class BookUserImpl implements Book {
#Override
public BookDAO getDao() {
return dao;
}
#Inject
#Override
public void setDao(BookDAO dao) {
this.dao = dao;
}
}
//Inject default implementation of resource
public class ApplicationResourcesModule extends AbstractModule
{
#Override
protected void configure()
{
bind(Book).to(BookImpl);
}
}
//But user wants to inject his implementation, so he bind it in users AbstractModule
public class ApplicationResourcesModuleUser extends AbstractModule
{
#Override
protected void configure()
{
bind(Book).to(BookUserImpl);
}
}
//Bind all resources
public class JerseyGuiceConfig extends GuiceServletContextListener
{
#Override
protected Injector getInjector()
{
//Override default binding by user bindings.
return Guice.createInjector(Modules.override(new ApplicationResourcesModule()).with(new ApplicationResourcesModuleUser()), new JerseyServletModule());
}
}
But unfortunately this doesn't work, while I can't bind jersey resources in guice like interface to implementation, only bind(BookImpl.class) work's. But such binding is impossible to overwrite. If I try to override bind(BookImpl.class) with bind(BookUserImpl.class) I get an error Conflicting URI templates. The URI template /books/{key} for root resource class. while #Path should be unique. So is there any solution for my use case?
i just wan't to warn you Modules.override does not work on Guice.createInjector(Stage.PRODUCTION,...) so you should use it carefully only for Development. You should create two context listeners and somehow (trough maven profiles lets say) setup the web.xml with proper implementation.
Better to use:
//Inject default implementation of resource
public class MainModule extends AbstractModule
{
#Override
protected void configure()
{
if(currentStage().equals(Stage.PRODUCTION) {
install(new ApplicationResourcesModuleUser());
} else {
install(new ApplicationResourcesModule());
}
}
}
//Bind all resources
public class JerseyGuiceConfigPROD extends GuiceServletContextListener
{
#Override
protected Injector getInjector()
{
//Override default binding by user bindings.
return Guice.createInjector(Stage.PRODUCTION, new MainModule(), new JerseyServletModule());
}
}
public class JerseyGuiceConfigDEV extends GuiceServletContextListener
{
#Override
protected Injector getInjector()
{
//Override default binding by user bindings.
return Guice.createInjector(Stage.DEVELOPMENT, new MainModule(), new JerseyServletModule());
}
}
You can use #ImplementedBy annotation to your interface to say the default implementation should be. So, you don't have to bind it explicitly and you, if you bind it, it will override the annotation binding.
#Path("/books/{key}")
#ImplementedBy(BookImpl.class)
public interface Book {
public BookDAO getDao();
#Inject //it is enough to put the injection here, i think
public void setDao(BookDAO dao);
}
I think this problem is not related to Book and Book implementations binding, but to a binding/registering of the servlets to the Jersey container. Could you paste whole stacktrace, the guice stacktraces are verbose and very helpful.

Categories

Resources