Java Deep Injection with custom annotations preferably with hk2 [duplicate] - 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

Spring Boot Custom Bean Loader

I am using JDBI in tandem with Spring Boot. I followed this guide which results in having to create a class: JdbiConfig in which, for every dao wanted in the application context, you must add:
#Bean
public SomeDao someDao(Jdbi jdbi) {
return jdbi.onDemand(SomeDao.class);
}
I was wondering if there is some way within Spring Boot to create a custom processor to create beans and put them in the application context. I have two ideas on how this could work:
Annotate the DAOs with a custom annotation #JdbiDao and write something to pick those up. I have tried just manually injecting these into the application start up, but the problem is they may not load in time to be injected as they are not recognized during the class scan.
Create a class JdbiDao that every repository interface could extend. Then annotate the interfaces with the standard #Repository and create a custom processor to load them by way of Jdbi#onDemand
Those are my two ideas, but I don't know of any way to accomplish that. I am stuck with manually creating a bean? Has this been solved before?
The strategy is to scan your classpath for dao interface, then register them as bean.
We need: BeanDefinitionRegistryPostProcessor to register additional bean definition and a FactoryBean to create the jdbi dao bean instance.
Mark your dao intercface with #JdbiDao
#JdbiDao
public interface SomeDao {
}
Define a FactoryBean to create jdbi dao
public class JdbiDaoBeanFactory implements FactoryBean<Object>, InitializingBean {
private final Jdbi jdbi;
private final Class<?> jdbiDaoClass;
private volatile Object jdbiDaoBean;
public JdbiDaoBeanFactory(Jdbi jdbi, Class<?> jdbiDaoClass) {
this.jdbi = jdbi;
this.jdbiDaoClass = jdbiDaoClass;
}
#Override
public Object getObject() throws Exception {
return jdbiDaoBean;
}
#Override
public Class<?> getObjectType() {
return jdbiDaoClass;
}
#Override
public void afterPropertiesSet() throws Exception {
jdbiDaoBean = jdbi.onDemand(jdbiDaoClass);
}
}
Scan classpath for #JdbiDao annotated interfaces:
public class JdbiBeanFactoryPostProcessor
implements BeanDefinitionRegistryPostProcessor, ResourceLoaderAware, EnvironmentAware, BeanClassLoaderAware, BeanFactoryAware {
private BeanFactory beanFactory;
private ResourceLoader resourceLoader;
private Environment environment;
private ClassLoader classLoader;
#Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
#Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
#Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false) {
#Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
// By default, scanner does not accept regular interface without #Lookup method, bypass this
return true;
}
};
scanner.setEnvironment(environment);
scanner.setResourceLoader(resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(JdbiDao.class));
List<String> basePackages = AutoConfigurationPackages.get(beanFactory);
basePackages.stream()
.map(scanner::findCandidateComponents)
.flatMap(Collection::stream)
.forEach(bd -> registerJdbiDaoBeanFactory(registry, bd));
}
private void registerJdbiDaoBeanFactory(BeanDefinitionRegistry registry, BeanDefinition bd) {
GenericBeanDefinition beanDefinition = (GenericBeanDefinition) bd;
Class<?> jdbiDaoClass;
try {
jdbiDaoClass = beanDefinition.resolveBeanClass(classLoader);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
beanDefinition.setBeanClass(JdbiDaoBeanFactory.class);
// Add dependency to your `Jdbi` bean by name
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference("jdbi"));
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(Objects.requireNonNull(jdbiDaoClass));
registry.registerBeanDefinition(jdbiDaoClass.getName(), beanDefinition);
}
}
Import our JdbiBeanFactoryPostProcessor
#SpringBootApplication
#Import(JdbiBeanFactoryPostProcessor.class)
public class Application {
}

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();
}
}
}
}

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.

Inject not working for nested objects[Jersey 2.22.1]

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);
}
});

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