Guice and generic factory - java

I'm new to guice, and using guice 4.1 I want to instantiate the following class:
public class GuiceMain<T> implements IGuiceMain<T> {
private ObjectWrapperFactory<T> factory;
#Inject
public GuiceMain(ObjectWrapperFactory<T> factory) {
this.factory = factory;
}
public void process(T t) {
ObjectWrapper wrapper = factory.create(t);
}
}
I dont have strong requirements how to create it, but I came to the solution to create interface GuiceMainFactory
public interface GuiceMainFactory {
<T> GuiceMain<T> create();
}
so my client code will look like this:
Injector injector = Guice.createInjector(new Configuration());
GuiceMain<File> instance = injector.getInstance(GuiceMainFactory.class).create();
File f = new File("text.txt");
instance.process(f);
and in my guice configuration class I write
install(new FactoryModuleBuilder().implement(IGuiceMain.class, GuiceMain.class).build(GuiceMainFactory.class))
But I get the error: 1) com.guice.GuiceMainFactory cannot be used as a key; It is not fully specified.
So I decided to write the implementation of GuiceMainFactory
public class GuiceMainFactoryImpl implements GuiceMainFactory {
private ObjectWrapperFactory objectWrapperFactory;
#Inject
public GuiceMainFactoryImpl(ObjectWrapperFactory objectWrapperFactory) {
this.objectWrapperFactory = objectWrapperFactory;
}
#Override
public <T> GuiceMain<T> create() {
return new GuiceMain<T>(objectWrapperFactory);
}
}
but in this case, on the line return new GuiceMain(objectWrapperFactory); I get unchecked assignment warning, because I cant specify generic type of objectWrapperFactory
I think I've over complicated everything, but I dont know how to deal with it

Related

Get a wildcard instance from Google Guice

Hi I am experimenting with Google Guice 5.x. I have my class defined as:
public class Foo<T> {
// some logic here
}
and it's being used in other classes like:
public class Bar {
private final Foo<Chocolate> provider;
public Bar(Foo<Chocolate> _choco) {
this.provider = _choco;
}
}
public abstract class BaseZoo { // in some other package in a different jar
private final Injector injector = Guice.createInjector(new MyAppModule());
private Foo<?> provider;
public String doSomething() {
if (provider == null)
this.provider = this.injector.getInstance(Foo.class);
// some other code logic.
}
}
Now, in my module file (MyAppModule) I have defined Foo as:
#Inject
#Provides
#Singleton
public Foo<Chocolate> getFoo(FooDependency fooDep) {
return new Foo<>(fooDep);
}
Now when I run my code, Google Guice is able to find an instance for Foo<Chocolate> but is not able to find an instance for Foo<?>.
Is there a way to configure Google Guice to resolve Foo<?> with an instance of Foo<Chocolate>?
Bind the type:
#Inject
#Provides
#Singleton
public Foo<?> getFoo(FooDependency fooDep) { ... }
If you still want to bind Foo<Chocolate>, use the #Provides method in the question, but also bind Foo<?> to it, you can do so in your configure method:
bind(new Key<Foo<?>>() {}).to(new Key<Foo<Chocolate>>() {});
or, with a provider method:
#Provides
Foo<?> provideWildcardFoo(Foo<Chocolate> chocolateFoo) {
return chocolateFoo;
}

Passing constructors for outer dependency into Guice implementation

I have a Job, which should read data from deep storage. I am using Guice DI for my project.
There is a deep store already written and coming as an outer dependencie. I am struggling with instantiating the client in Guice
Here is the code
JobModule
public class JobModule extends AbstractModule {
private Config config;
JobModule(Config config) {
this.config = config;
}
#Override
protected void configure() {
bind(Reader.class).to(DeepStoreReader.class);
}
#Provides
#Named("config")
Config provideConfig() {
return this.config;
}
}
Reader Interface
public interface Reader {
List<String> getData(String path);
}
DeepStoreReader
public class DeepStoreReader implements Reader {
private final DeepStoreClient deepStoreClient;
DeepStoreReader(#Named("config") Config config) {
this.deepStoreClient = new DeepStoreClient(config);
}
#Override
public List<String> getData(String path) {
return this.deepStoreClient.getData(path);
}
}
The issue is I don't want to instantiate DeepStoreClient inside the DeepStoreReader constructor, because it becomes difficult to test DeepStoreReader, since I won't be able to mock DeepStoreClient
What is the preferred way to instantiate a client in such cases? DeepStoreClient is not a Guice module/implementation and is coming as an outer published dependency
PS: I am new to DI and learning Guice
What you want is constructor injection, e.g.:
#Inject
public DeepStoreReader(DeepStoreClient deepStoreClient) {
this.deepStoreClient = deepStoreClient;
}
Guice will take care of instantiating the DeepStoreClient for you.
EDIT:
If DeepStoreClient itself has dependencies, you can also annotate that constructor:
#Inject
public DeepStoreClient(#Named("config") Config config) {
// ... 8< ...
}

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

Spring proxy to choose implementation based on annotation and runtime value

I would like to inject a proxy implementation of an interface to a component and then let spring choose the right implementation based on a runtime property (and the value of an annotation at the implementation class). So my component does not have to care about choosing the right one.
It is kind of like a scope. But i think scopes are only for handling different instances of the same implementation class. Am i wrong with this?
I would like this to run for arbitrary interfaces without creating a service locator or some other construct for every new service.
Here is an example.
Suppose I have an interface defining a service
package test;
public interface IService {
void doSomething();
}
and two implementations:
package test;
import javax.inject.Named;
#Named
#MyAnnotation("service1")
public class Service1 implements IService {
#Override
public void doSomething() {
System.out.println("this");
}
}
...
package test;
import javax.inject.Named;
#Named
#MyAnnotation("service2")
public class Service2 implements IService {
#Override
public void doSomething() {
System.out.println("that");
}
}
Now I would like to inject an IService to another component and let spring choose the correct implementation based on some queryable run time property and the value of MyAnnotation.
Is there a way to do this in a general way in spring?
EDIT:
I have a Context that holds some value. It is a thread local in this case.
package test;
public class MyValueHolder {
private static final ThreadLocal<String> value = new ThreadLocal<>();
public static void set(String newValue) {
value.set(newValue);
}
public static String get() {
return value.get();
}
public static void reset() {
value.remove();
}
}
And I have an component which uses IService
package test;
import javax.inject.Inject;
import javax.inject.Named;
#Named
public class MyComponent {
#Inject
private IService service;
public void myImportantWorkflow(){
MyValueHolder.set("service1");
service.doSomething();
MyValueHolder.set("service2");
service.doSomething();
}
}
The injected service should only be a proxy. Depending on the value set in MyValueHolder the call to doSomething should delegate to service1 or service2. So in this example it should delegate to doSomething on service1 in the first call and to service2 in the second call.
I could write such a delegator implementing the IService interface and use it for this one service. But then i have to repeat this for every other service . I hoped spring could do something like this with proxies almost by itself. Of course i have to provide some method to look beans up based on the value hold in the thread local and register it to spring. But i have no idea if that is even possible without modifying the spring framework. And if it is possible how to accomplish this.
You could use a ProxyFactoryBean to create the proxies and a TargetSource to do the lookup.
For example (not tested)
public class AnnotatedBeanTargetSource implements TargetSource, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
private Class<? extends Annotation> annotationType;
private Class<?> implementedIterface;
private Map<String, Object> beans;
#Override
public Class<?> getTargetClass() {
return this.implementedIterface;
}
#Override
public boolean isStatic() {
return false;
}
#Override
public Object getTarget() throws Exception {
if (this.beans == null) {
this.beans = lookupTargets();
}
return this.beans.get(MyValueHolder.get());
}
protected Map<String, Object> lookupTargets() {
Map<String, Object> resolvedBeans = new HashMap<String, Object>();
String[] candidates = beanFactory.getBeanNamesForAnnotation(annotationType);
for (String beanName : candidates) {
Class<?> type = beanFactory.getType(beanName);
if (this.implementedIterface.isAssignableFrom(type)) {
Annotation ann = AnnotationUtils.getAnnotation(type, annotationType);
resolvedBeans.put((String) AnnotationUtils.getValue(ann), beanFactory.getBean(beanName));
}
}
return resolvedBeans;
}
#Override
public void releaseTarget(Object target) throws Exception {
// nothing to do
}
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
public Class<? extends Annotation> getAnnotationType() {
return annotationType;
}
public void setAnnotationType(Class<? extends Annotation> annotationType) {
this.annotationType = annotationType;
}
public Class<?> getImplementedIterface() {
return implementedIterface;
}
public void setImplementedIterface(Class<?> implementedIterface) {
this.implementedIterface = implementedIterface;
}
}
This is what I would do:
#Named
public class MyComponent {
// introduce a marker interface for Injecting proxies
#InjectDynamic
IService service
...
public void useIService() {
service.doSomething();
...
service.doSomethingElse();
...
service.doFinally();
}
}
Define a BeanPostProcessor that scans for bean with fields annotated with #InjectDynamic, then creates and inject a Proxy implementing the type required by the field.
The Proxy implementation will look in the applicationContext for beans implementing Supplier<T> (Java 8 or guava versions) where <T> is the type of the field annotated with #InjectDynamic.
Then you can define
#Name
public IServiceSupplier implements Supplier<IService> {
#Override
public IService get() {
// here you implement the look-up logic for IService
}
}
In this way the look-up of active the current implementation is decoupled from the Proxy and can be change by target type.

How to retrieve annotated instance from Guice's injector?

Let's say I have a module:
Module extends AbstractModule
{
#Override
protected void configure()
{
bind(String.class).
annotatedWith(Names.named("annotation")).
toInstance("DELIRIOUS");
}
}
and I want to test the module and check if it injects the right value in a String field annotated with Names.named("annotation") without having a class and a field but obtaining the value directly from the injector:
#Test
public void test()
{
Injector injector = Guice.createInjector(new Module());
// THIS IS NOT GOING TO WORK!
String delirious = injector.getInstance(String.class);
assertThat(delirious, IsEqual.equalTo("DELIRIOUS");
}
injector.getInstance(Key.get(String.class, Names.named("annotation")));
I'm using the following method
public <T> T getInstance(Class<T> type, Class<? extends Annotation> option) {
final Key<T> key = Key.get(type, option);
return injector.getInstance(key);
}
for this. In general, you still have the problem of creating the annotation instance, but here Names.named("annotation") works.

Categories

Resources