Guice how can I provide different subclasses instance depending on String id - java

I have a Factory Class use case I want to implement with Guice, but not sure how.
I have an Abstract Class named Action which represent different kind of actions the user could perform on my app.
Each of the Actions are subclasses of Action class, and each of them also have an identification of String type.
Because Actions are heavy objects I don't want to have it all instanciated at once, so I provides a Factory to instanciate each of them depending on the ID the client ask for.
The Factory Interface looks like:
public interface ActionFactory {
Action getActionByID(String id);
}
Our implementation of this Factory uses a HashMap to maintain the relationship between the String instance and a so called ActionInstantiator that will provides the concrete Action instance.
Implementation of this looks like:
public class ActionFactoryImpl implements ActionFactory {
private HashMap<String, ActionInstantiator> actions;
private static ActionFactoryImpl instance;
protected ActionFactoryImpl(){
this.actions=new HashMap<String, ActionInstantiator>();
this.buildActionRelationships();
}
public static ActionFactoryImpl instance(){
if(instance==null)
instance=new ActionFactoryImpl();
return instance;
}
public Action getActionByID(String id){
ActionInstantiator ai = this.actions.get(id);
if (ai == null) {
String errMessage="Error. No action with the given ID:"+id;
MessageBox.alert("Error", errMessage, null);
throw new RuntimeException(errMessage);
}
return ai.getAction();
}
protected void buildActionRelationships(){
this.actions.put("actionAAA",new ActionAAAInstantiator());
this.actions.put("actionBBB",new ActionBBBInstantiator());
.....
.....
}
}
So some client that could use this factory and wants ActionAAA instance class calls it like this:
Action action=ActionFactoryImpl.instance().getActionByID(actionId);
Where actionId was obtained at runtime from database.
I found that some kind of annotation injection could do something similar, but in my case I think that that wouldn't work, because I only know the instance that the user will requieres at runtime, so I couldn't annotated on the code.
I'm new to Guice so maybe this is something very common I couldn't found in the docs, I appologies if that is the case.
Any help will be appreciated.
Regards
Daniel

You want to use the Multibindings extension, specifically MapBinder. You probably want your ActionInstantiator type to implement Provider<Action>. Then you can do:
MapBinder<String, Action> mapbinder
= MapBinder.newMapBinder(binder(), String.class, Action.class);
mapbinder.addBinding("actionAAA", ActionAAAInstantiator.class);
// ...
Then you can inject a Map<String, Provider<Action>> where you want. You'll also be able to inject things in to your ActionInstantiators.

Related

Assisted inject to inject different dependencies based on runtime check

I have an application class "Application", one abstract class "AbstractClass" extended by "Impl1" and "Impl2".
The application class gets the impl1 or impl2 to perform some task based on the input it receives.
Currently I am injecting both the classes into the application class as shown below.
Then based on input, I either ask impl1 OR impl2 to perform the task.
public class Application {
private static final Data data1 = DATA_CONFIG.data_1;
private final AbstractClass impl1;
private final AbstractClass impl2;
#Inject
Application(final AbstractClass impl1, final AbstractClass impl2){
this.impl1 = impl1;
this.impl2 = impl2;
}
public void mainTask(final Data data){
if(data == data1){
impl1.performTask();
}else{
impl2.performTask();
}
}
}
But, is there any way I could use assisted inject or a similar concept to inject only the dependency required, so for example input is data1, I only inject impl1 and not impl2.??
So, what you want is to select injected object depending to some context of injection point - value of Data object in particular. I didn't do such things and can't guarantee success, but you can try custom injections.
Also you can do something like factory. But IMHO, this approach is not much better than original, cause it will just move selection between impl1 and impl2 to a factory class you have to create first.
Sketch:
#Inject
Application(IAbstractClassFactory factory){
this.factory = factory
}
void mainTask(final Data data){
impl = factory.create(data)
}

How to configure providers with custom parameters?

My class depends on some services which needs to take few parameters and then make network call, currently I am passing those parameters and then creating those services via a factory injected into my class. I need to inject those services as a dependency instead, I know that I can create providers for them but in most of the examples I see that the providers are often bound to the fixed values like serveraddres etc. but I need to give then values during run time.
Below is my example code:
public SomeClass {
private final SomeFactory someFactory;
#Inject
SomeClass(SomeFactory factory) {
someFactory = factory;
}
public Foo getFoo(String fooId) {
FooService fooService = someFactory.getFooService(fooId);
return fooService.getFoo();
}
}
What I need to do is:
public SomeClass {
private final FooService fooService;
#Inject
SomeClass(FooService fooService) {
this.fooService = fooService;
}
public Foo getFoo(String fooId) {
return fooService.getFoo();
}
}
Update 1
Making the use case more clear:
#Provides
#RequestScoped
public SomeService provideSomeService(Dep1 dep1, String code) throws IOException {
return new SomeService.Builder()
.withApplicationName("Foo")
.setCode(code)
.build();
}
Here, code can be null by default and when needed I can give some value in it.
Can I somehow pass arguments to the provider before its created?
If you have a binding for your value (here, code is a String without a binding annotation), then your Update 1 is exactly what the code would look like.
In practice, there are a few differences:
Constants like int and String values are generally annotated with a binding annotation, either #Named or a custom annotation.
If you need to inject a value into an object graph after Guice initialization, but have a deep enough object graph that dependency injection is still a good idea, you can create a child injector. This way you can make a #Named("code") String accessible within one action or object, but not across your entire Guice application.
If your value for code is dynamic enough that it can't be provided through Guice as a key of its own, then you'll have to pass it in using a factory of some sort. For a Builder-based object, I'd say that your SomeFactory implementation is the best that I would come up with in your case.
If you don't need to use a Builder, and can let Guice create the object based on your fields or constructor parameters, you can code-generate a Factory.
Guice can generate a factory for you through FactoryModuleBuilder, in a feature known as "assisted injection".
Google's other tool, AutoFactory, will code-generate a factory implementation that works in both Guice and Dagger. (It's bundled as "Auto", which includes a model object generator called AutoValue that also generates annotation implementations.)
I put a small demonstration of a child injector and assisted injection in my other SO answer here.
The best approach here is to parameterize the module and pass the parameter through to a provider that you create at runtime:
public class MyModule extends AbstractModule {
private final String code;
public MyModule(String code) {
this.code = code;
}
#Override public void configure() {
Provider<Dep1> depProvider = getProvider(Dep1.class);
bind(SomeService.class)
.toProvider(() -> new SomeService.Builder()
.withApplicationName("Foo")
.withDep(depProvider.get())
.setCode(code)
.build())
.in(RequestScoped.class);
}
}

Guice: One "Provider<T>" for multiple implementations

I have an interface that has 20 or so annotated implementations. I can inject the correct one if I know which I need at compile time, but I now need to dynamically inject one based on runtime parameters.
As I understood the documentation, I would have to use 20 or so Provider<T> injections and then use the one I need, which seems rather excessive to me. Is there a way to have something like an inst(Provider<T>).get(MyAnnotation.class) to bind a specific implementation, and then have only that Provider injected into my class?
Inject a MapBinder.
In your module, load the bindings into the MapBinder, then make your runtime parameters injectable as well. This example is based on the one in the documentation:
public class SnacksModule extends AbstractModule {
protected void configure() {
MapBinder<String, Snack> mapbinder
= MapBinder.newMapBinder(binder(), String.class, Snack.class);
mapbinder.addBinding("twix").to(Twix.class);
mapbinder.addBinding("snickers").to(Snickers.class);
mapbinder.addBinding("skittles").to(Skittles.class);
}
}
Then, in your object, inject the Map and the parameter. For this example I will assume you've bound a java.util.Properties for your runtime parameters:
#Inject
public MyObject(Map<String, Provider<Snack>> snackProviderMap, Properties properties) {
String snackType = (String) properties.get("snackType");
Provider<Snack> = snackProviderMap.get(property);
// etc.
}
Note, with the same MapBinder you can inject either a simple Map<String, Snack> or a Map<String, Provider<Snack>>; Guice binds both.
If all you want is to get an instance programmatically, you can inject an Injector. It's rarely a good idea--injecting a Provider<T> is a much better idea where you can, especially for the sake of testing--but to get a binding reflectively it's the only way to go.
class YourClass {
final YourDep yourDep; // this is the dep to get at runtime
#Inject YourClass(Injector injector) {
YourAnnotation annotation = deriveYourAnnotation();
// getProvider would work here too.
yourDep = injector.getInstance(Key.get(YourDep.class, annotation));
}
}
If you're trying write a Provider that takes a parameter, the best way to express this is to write a small Factory.
class YourDepFactory {
#Inject #A Provider<YourDep> aProvider;
#Inject #B Provider<YourDep> bProvider;
// and so forth
Provider<YourDep> getProvider(YourParameter parameter) {
if (parameter.correspondsToA()) {
return aProvider;
} else if (parameter.correspondsToB()) {
return bProvider;
}
}
YourDep get(YourParameter parameter) {
return getProvider(parameter);
}
}

can guice create a factory for me?

I have the following factory class:
public class MessagePresenterCreator implements IPresenterFactory{
#Override
public MessagePresenter createPresenter(Message m) {
if (m instanceof Letter) {
return new LetterPresenter();
}
if (m instanceof Cable) {
return new CablePresenter();
}
if (m instanceof Postcard) {
return new PostcardPresenter();
}
throw new IllegalArgumentException();
}
}
Can I configure a guice to auto-generate and supply such factory by the following interface:
public interface IPresenterFactory {
public abstract MessagePresenter createPresenter(Message m);
}
I like to think of factories in two ways - factories that merely assemble objects from components, and factories that do conditional logic in that assembly.
Your factories have logic behind them. Guice can't automate that logic, because it simply handles the wiring up of dependencies. If you have a type that is created that needs some amount of injected dependencies and some things that are provided just at creation time, then the auto-wiring of factories can be done with guice's AssistedInject extension. This would let you give a factory interface, annotate any fields in the created type with #AssistedInject, and guice's extension would create a factory implementation class which would inject anything bound in the Injector, and also pass through those create() parameters. But in such a case, all Guice and AssistedInject are doing is pulling pieces together - wiring things up according to a recipe specified in advance. It's not making decisions about them at the last minute.
You're providing conditional creation of objects. That's not going to work.
An idea above mentions making a factory that depends on mapping of type to presenter - something like:
Map<Class<? extends Message>, Class<? extends MessagePresenter>>
This is a good approach, if you combine it with Multibinder's MapBindings. (I gotta beef up those docs... hmm)
With this approach you can create an extensible factory - defining the initial mappings of Message -> MessagePresenter subclasses, but leave the possibility open for additional mappings later without having to change your factory - just bind more mappings on the multibinder, like so:
MapBinder<String, Snack> mapbinder = MapBinder.newMapBinder(
binder(),
new TypeLiteral<Class<? extends Message>>(){},
new TypeLiteral<Class<? extends MessagePresenter>>(){});
mapbinder.addBinding(MyMessage.class).toInstance(MyMessagePresenter.class);
mapbinder.addBinding(YourMessage.class).toInstance(YourMessagePresenter.class);
And you can do this in as many modules as you like, adding more types between which to switch, using the mapping.
No guice does not have a factory like that built in. You could write a factory that took a Map<Class<? extends Message>, Class<? extends MessagePresenter> and have that use reflection. That way you can manage it in a guice module if that is your goal.
public class MessagePresenterCreator implements IPresenterFactory{
private final Map<Class<? extends Message>, Class<? extends MessagePresenter> mapping;
public MessagePresenterCreator(Map<Class<? extends Message>, Class<? extends MessagePresenter> mapping) {
this.mapping = mapping;
}
#Override
public MessagePresenter createPresenter(Message m) {
Class<? extends MessagePresenter> clazz = mapping.get(m);
if (clazz == null) {
throw new UnsupportedOperationException();
}
return clazz.newInstance();
}
}

How to #Inject into existing object hierarchy using Guice?

I have an existing object hierarchy where some objects have fields that need to be injected. Also there are some other objects that are constructed using Google Guice and need to be injected with references to some objects from previously described object hierarchy. How do I do such kind of injection with Guice?
The problem is that objects from existing hierarchy were not constructed using Guice, and therefore are not subject to inject process by default. There is, of course injector.injectMembers() method that is able to inject into existing object instance, but it does not work on object hierarchies.
For those wondering why I can't build mentioned object hierarchy using Guice. This hierarchy represents GUI objects and is built by a GUI framework (Apache Pivot) from a declarative GUI description (in fact this process can be described as object deserialization). That way interface construction is rather simple, and I only want to inject certain service references into interface objects and vice versa (for callbacks).
Approach I am currently about to take is described below.
For injecting into preexisting object hierarchy just let all objects that are interested in injection implement certain interface, like:
public interface Injectable {
void injectAll(Injector injector);
}
Those objects would then implement this interface like so:
public void injectAll(Injector injector) {
injector.injectMembers(this);
for (Injectable child : children)
child.injectAll(injector);
}
Then I'd just call mainWindow.injectAll(injector) for root object in hierarchy and all objects of interest are injected.
Not very nice solution, but gets the work done on one side. On the other side, I need to inject objects from this hierarchy. I guess it can be done via implementing custom provider for such objects.
Is there a better solution to my problem? Maybe there is also something wrong with my approach?
This solution will work, but I'd like to propose a slightly different one to you.
Specifically, since you're going to traverse a deep object structure, this really looks like a job for the Visitor pattern. Also, what you're describing seems to call out for a two-stage injector: a "bootstrap" stage that can inject stuff needed by the pivot-created hierarchy (but can't inject any pivot-created elements) and a second stage that is the real injector used by your app (that can inject anything).
What I would suggest is this basic pattern: make a visitor that traverses the hierarchy and, as it goes, it does injection on those things that need it and records those things that need to be injected elsewhere. Then, when it is done visitng everything, it uses Injector.createChildInjector to make a new Injector that can inject stuff from the original Injector and stuff from the pivot-created hierarchy.
First define a visitor that can hit everything in this hierarchy:
public interface InjectionVisitor {
void needsInjection(Object obj);
<T> void makeInjectable(Key<T> key, T instance);
}
Then define an interface for all your pivot-created elements:
public interface InjectionVisitable {
void acceptInjectionVisitor(InjectionVisitor visitor);
}
You'd implement this interface in your pivot-created classes as (assuming this code in the FooContainer class):
public void acceptInjectionVisitor(InjectionVisitor visitor) {
visitor.needsInjection(this);
visitor.makeInjectable(Key.get(FooContainer.class), this);
for (InjectionVisitable child : children) {
child.acceptInjectionVisitor(visitor);
}
}
Note that the first two statements are optional - it may be that some objects in the pivot hierarchy don't need injection and it could also be that some of them you wouldn't want to have injectable later. Also, notice the use of Key - this means that if you want some class to be injectable with a particular annotation you can do something like:
visitor.makeInjectable(Key.get(Foo.class, Names.named(this.getName())), this);
Now, how do you implement InjectionVisitor? Here's how:
public class InjectionVisitorImpl implements InjectionVisitor {
private static class BindRecord<T> {
Key<T> key;
T value;
}
private final List<BindRecord<?>> bindings = new ArrayList<BindRecord<?>>();
private final Injector injector;
public InjectionVisitorImpl(Injector injector) {
this.injector = injector;
}
public void needsInjection(Object obj) {
injector.injectMemebers(obj);
}
public <T> void makeInjectable(Key<T> key, T instance) {
BindRecord<T> record = new BindRecord<T>();
record.key = key;
record.value = instance;
bindings.add(record);
}
public Injector createFullInjector(final Module otherModules...) {
return injector.createChildInjector(new AbstractModule() {
protected void configure() {
for (Module m : otherModules) { install(m); }
for (BindRecord<?> record : bindings) { handleBinding(record); }
}
private <T> handleBinding(BindRecord<T> record) {
bind(record.key).toInstance(record.value);
}
});
}
}
You then use this in your main method as:
PivotHierarchyTopElement top = ...; // whatever you need to do to make that
Injector firstStageInjector = Guice.createInjector(
// here put all the modules needed to define bindings for stuff injected into the
// pivot hierarchy. However, don't put anything for stuff that needs pivot
// created things injected into it.
);
InjectionVisitorImpl visitor = new InjectionVisitorImpl(firstStageInjector);
top.acceptInjectionVisitor(visitor);
Injector fullInjector = visitor.createFullInjector(
// here put all your other modules, including stuff that needs pivot-created things
// injected into it.
);
RealMainClass realMain = fullInjector.getInstance(RealMainClass.class);
realMain.doWhatever();
Note that the way createChildInjector works ensures that if you have any #Singleton things bound in the stuff injected into the pivot hierarchy, you'll get the same instances injected by your real injector - the fullInjector will delegate injectoion to the firstStageInjector so long as the firstStageInjector is able to handle the injection.
Edited to add: An interesting extension of this (if you want to delve into deep Guice magic) is to modify InjectionImpl so that it records the place in your source code that called makeInjectable. This then lets you get better error messages out of Guice when your code accidentally tells the visitor about two different things bound to the same key. To do this, you'd want to add a StackTraceElement to BindRecord, record the result of new RuntimeException().getStackTrace()[1] inside the method makeInjectable, and then change handleBinding to:
private <T> handleBinding(BindRecord<T> record) {
binder().withSource(record.stackTraceElem).bind(record.key).toInstance(record.value);
}
You could inject MembersInjectors to inject nested fields. For example, this will deeply inject an existing Car instance:
public class Car {
Radio radio;
List<Seat> seats;
Engine engine;
public Car(...) {...}
#Inject void inject(RadioStation radioStation,
MembersInjector<Seat> seatInjector,
MembersInjector<Engine> engineInjector) {
this.radio.setStation(radioStation);
for (Seat seat : seats) {
seatInjector.injectMembers(seat);
}
engineInjector.injectMembers(engine);
}
}
public class Engine {
SparkPlug sparkPlug;
Turbo turbo
public Engine(...) {...}
#Inject void inject(SparkPlug sparkplug,
MembersInjector<Turbo> turboInjector) {
this.sparkPlug = sparkPlug;
turboInjector.injectMembers(turbo);
}
}

Categories

Resources