Injecting Map of customized objects in Guice - java

I am new to Guice. I have written following module:
public class TransactionConfigModule extends AbstractModule {
#Override
protected void configure() {
MapBinder<String, ITransactionManager> transactionModeMap = MapBinder
.newMapBinder(binder(), String.class, ITransactionManager.class
, Names.named("TransactionManagerMap"));
transactionModeMap.addBinding(TransactionMode.CREDIT.platformName())
.to(CreditCardManager.class)
.asEagerSingleton();
transactionModeMap.addBinding(TransactionMode.DEBIT.platformName())
.to(DebitCardManager.class)
.asEagerSingleton();
}
}
And then I have created the injector (which has been there already) as follows:
Injector injector = createInjector(
new PaymentModule()
);
PaymentModule (which has been already there) is like following:
public class RBSAVSTechServiceModule extends AbstractModule {
...
protected void configure(){
install(new TransactionConfigModule()); <--- I just added this line in the existing code
}
}
Here I am getting following exception:
No implementation for java.util.Map<java.lang.String, ITransactionManager> was bound.
I am not getting any idea regarding how to fix this.

From the example above it is not clear where you are installing RBSAVSTechServiceModule. I assume it is done inside PaymentModule.
I also note that you created the map binder using a binding annotation. Are you sure that your injection point is using the corresponding #Named("TransactionManagerMap")? The error message seems to indicate it is not.

Related

How to inject dependencies if your module itself take parameters in as a constructor?

I have just learned about Dependency injection (DI) and I am beginning to like it. To inject dependencies I am using Google Guice framework. Everything was running conceptually fine but while writing a module a thought came to my mind that what if my module require dependencies as a constructor, after all, it is just a class extending AbstractModule.
So, basically, I have 3 modules as a whole.
Environment Module
public class EnvModule extends AbstractModule {
#Override
protected void configure() {
install(new Servicemodule());
}
}
ServiceModule
public class ServiceModule extends AbstractModule {
private final boolean isEnabled;
#Override
protected void configure() {
if (isEnabled) {
install (new ThirdModule());
}
}
ThirdModule (It does not take any arguments in any constructor and have some bindings of its own)
Basically, the variable in the service module defines whether my application needs to install the third module or not. And that variable is defined in an application configuration file. So how do I inject that variable in the ServiceModule? As the field is final, setter injection is not possible, is there a way to use construction injection or field injection to inject the value.
I see the following options:
Use system variable:
ServiceModule() {isEnabled = System.getProperty("isThirdModuleEnabled")};
Read the config file directly in the ServiceModule() constructor
Use #Provides:
class ServiceModule ... {
#Provide #Singleton ThirdModuleParam getThirdModuleParam(...) {
//read the config file
ThirdModuleParam res = new ThirdModuleParam();
res.setIsEnabed(...);
return res;
}
}
class ThirdModule {
#Provide SomeThirdModuleClass getIt(ThirdModuleParam param) {
return param.isEnabled() ? new SomeThirdModuleClass() : null;
}

Empty Multibinder/MapBinder in Guice

In the process of building a plugin architecture using Guice's MapBinder, using Guice 3.0, I've run into the issue that Guice throws a CreationException when stripped of all modules, which is a viable configuration in this application. Is there a way to get Guice to inject an empty Map? Or, by extension, an empty set with Multibinder?
For example:
interface PlugIn {
void doStuff();
}
class PlugInRegistry {
#Inject
public PlugInRegistry(Map<String, PlugIn> plugins) {
// Guice throws an exception if OptionalPlugIn is missing
}
}
class OptionalPlugIn implements PlugIn {
public void doStuff() {
// do optional stuff
}
}
class OptionalModule extends AbstractModule {
public void configure() {
MapBinder<String, PlugIn> mapbinder =
MapBinder.newMapBinder(binder(), String.class, PlugIn.class);
mapbinder.addBinding("Optional").to(OptionalPlugIn.class);
}
}
In the documentation for MapBinder, it says:
Contributing mapbindings from different modules is supported. For example, it is okay to have both CandyModule and ChipsModule both create their own MapBinder, and to each contribute bindings to the snacks map. When that map is injected, it will contain entries from both modules.
So, what you do is, don't even add the entry in your basic module. Do something like this:
private final class DefaultModule extends AbstractModule {
protected void configure() {
bind(PlugInRegistry.class);
MapBinder.newMapBinder(binder(), String.class, PlugIn.class);
// Nothing else here
}
}
interface PlugIn {
void doStuff();
}
Then, when you create your injector, if the additional modules exist, great! Add them. If they don't exist, then don't add them. In your class, do this:
class PlugInRegistry {
#Inject
public PlugInRegistry(Map<String, PlugIn> plugins) {
PlugIn optional = plugins.get("Optional");
if(optional == null) {
// do what you're supposed to do if the plugin doesn't exist
}
}
}
Note: You have to have the empty MapBinder, or the Map injection won't work if there are no optional modules present.

Guice TypeListener not notified about the injected class Type

I have the following Guice module:
class MyModule extends AbstractModule {
#Override
protected void configure() {
bindListener(Matchers.any(), new TypeListener() {...});
}
#Provides
SomeClass createSomeClass(final Parameter param) {
log(param.getValue()); <-- this gets logged
...
}
}
What I found strange is that my TypeListener doesn't get notified about the Parameter type. Even though the provider is beign called and returns SomeClass. I also see the log statement so clearly the Parameter was injected by Guice.
#Override
protected void configure() {
bind(Parameter.class);
bindListener(Matchers.any(), new TypeListener() {...});
}
I'm aware of Untargetted bindings and the statement:
An untargetted binding informs the injector about a type, so it may prepare dependencies eagerly.
I would still expect Guice to call the TypeListener for any type which is either explicitly binded or injected for the first time.
So do I need to make untargetted binding for such classes as a rule of thumb?
NOTE: marking the Parameter constructor with #Inject doesn't solve the problem.
EDIT:
The complete example (hope I din't leave too much garbage) is as follows:
public class TestGuice {
public static void main(String[] args) {
Injector parentInjector = Guice.createInjector(new ParentModule());
Injector childInjector = parentInjector.createChildInjector(new SubModule());
childInjector.getInstance(Runnable.class).run();
}
static class ParentModule extends AbstractModule {
#Override
protected void configure() {
}
}
static class SubModule extends AbstractModule {
#Override
protected void configure() {
bind(SampleInjectedClass.class); // <-- Comment/uncomment here
final TypeListener typeListener = new TypeListener() {
public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
System.out.println("Type: " + type.getRawType());
}
};
bindListener(Matchers.any(), typeListener);
}
#Provides
Runnable createRunnable(final SampleInjectedClass sampleClass) {
return new Runnable() {
#Override
public void run() {
sampleClass.test();
}
};
}
}
static class SampleInjectedClass {
public void test() {
System.out.println("Test");
}
}
}
If the line is present the output is:
Type: class com.barcap.test.TestGuice$SampleInjectedClass
Type: class com.google.inject.internal.ProviderMethod
Test
If I remove the line I get:
Type: class com.google.inject.internal.ProviderMethod
Test
I noticed that if the injector wasn't created via the createChildInjector code the bind(...) is not necessary.
Just-in-time bindings created for child injectors will be created in an ancestor injector whenever possible. This means that, without the bind(SampleInjectedClass.class); line, the binding for SampleInjectedClass is created in the parent injector. Since the parent injector doesn't have your TypeListener, it won't be triggered.
Can you check your code? In my test on Guice 3, I couldn't reproduce what you're seeing.
Also, from the TypeListener docs, emphasis mine and typo sic:
public abstract void hear (TypeLiteral<I> type, TypeEncounter<I> encounter)
Invoked when Guice encounters a new type eligible for constructor or members injection. Called during injector creation (or afterwords if Guice encounters a type at run time and creates a JIT binding).
Though all the rest of your dependencies will be called on the TypeListener immediately when the injector is created, implicit ("just-in-time") bindings will not.
However, based on my example above, it appears that when a parameter is included in a provider method, it is registered on the same listener immediately. Can you produce a short self-contained example that shows the behavior you're asking about?

Guice - Default binding definition

Is there a way to declare default binding in Guice 3.0 ?
Here is an example of what I expected :
//Constructor for Class Impl1
#Inject
public Impl1 (#One IMyOwn own)
{
...
}
//Constructor for Class Impl2
#Inject
public Impl2 (#Two IMyOwn own)
{
...
}
//Declare a default binding
bind(IMyOwn.class).to(DefaultMyOwn.class);
//Then, if I want to bind a custom implementation for #Two
bind(IMyOwn.class).annotatedWith(Two.class).to(TwoMyOwn.class);
Actually, this example can't works because I must declare a binding for all annotation (#One, #Two).
Are there solutions to do that with Guice ?
Thanks.
Use the #Named binding.
From Guice Reference on Github:
Guice comes with a built-in binding annotation #Named that uses a string:
public class RealBillingService implements BillingService {
#Inject
public RealBillingService(#Named("Checkout") CreditCardProcessor processor) {
...
}
To bind a specific name, use Names.named() to create an instance to pass to annotatedWith:
bind(CreditCardProcessor.class)
.annotatedWith(Names.named("Checkout"))
.to(CheckoutCreditCardProcessor.class);
So in your case,
//Constructor for Class Impl1
#Inject
public Impl1 (#Named("One") IMyOwn own)
{
...
}
//Constructor for Class Impl2
#Inject
public Impl2 (#Named("Two") IMyOwn own)
{
...
}
and your module will look like:
public class MyOwnModule extends AbstractModule {
#Override
protected void configure() {
bind(IMyOwn.class)
.annotatedWith(Names.named("One"))
.to(DefaultMyOwn.class);
bind(IMyOwn.class)
.annotatedWith(Names.named("Two"))
.to(TwoMyOwn.class);
}
}
With Guice 4.X there is Optional Binder.
public class FrameworkModule extends AbstractModule {
protected void configure() {
OptionalBinder.newOptionalBinder(binder(), Renamer.class);
}
}
public class FrameworkModule extends AbstractModule {
protected void configure() {
OptionalBinder.newOptionalBinder(
binder(),
Key.get(String.class, LookupUrl.class))
.setDefault().toInstance(DEFAULT_LOOKUP_URL);
}
}
In Guice 3.0 you may be able to exploit the automatic binding of the default constructor.
Use a single #Inject or public no-arguments constructor.
But this has constraints, as your default constructor needs to be of the same concrete class so derivation may become cumbersome.
Guice tries to check as much of your configuration (aka. Binding) as possible. This also means, that Guice cannot tell whether a missing binding for #One is an error or should map to some default case.
If you are interested in the details, lookup the BindingResolution sequence in Guice. Since step 4 and step 6 deal with binding annotation and step 6 explicitly forbids default, I think you are out of luck.
.6. If the dependency has a binding annotation, give up. Guice will not create default bindings for annotated dependencies.
So the best you can do is to provide Guice with a hint, that #One should map to the default like this:
bind(IMyOwn.class).annotatedWith(One.class).to(IMyOwn.class);
So you do not need to state the concrete default class DefaultMyOwn multiple times.

How to specify default Enum instance for Guice?

I'd need something like
#DefaultInstance(Level.NORMAL)
enum Level {NORMAL, FANCY, DEBUGGING}
which would make Guice to return Level.NORMAL for the expression
injector.getInstance(Level.class)
There's no such thing like #DefaultInstance. As a workaround I've tried #ProvidedBy with a trivial Provider, but it doesn't work.
Maybe overriding modules could help you. A default level can be configured using AppLevel module:
public class AppModule extends AbstractModule {
#Override
public void configure() {
bind(Level.class).toInstance(Level.NORMAL);
// other bindings
}
}
and a specific one can be configured in a small overriding module:
public class FancyLevelModule extends AbstractModule {
#Override
public void configure() {
bind(Level.class).toInstance(Level.FANCY);
}
}
At the end just create an injector overriding the AppModule with a specific Level config:
public static void main(String[] args) {
Injector injector =
Guice.createInjector(
Modules.override(new AppModule()).with(new FancyLevelModule())
);
System.out.println("level = " + injector.getInstance(Level.class));
}
UPDATE
This problem can be solved in a bit different way. Let's say that Level is used in a class as an injected field:
class Some
{
#Injected(optional = true)
private Level level = Level.NORMAL;
}
A default level will be initialized as part of the creation of instances of Some. If some Guice config module declares some other level it will be optionally injected.
A solution, but unfortunatelly not using annotations, would be:
enum Level
{
NORMAL, FANCY, DEBUGGING;
static final Level defaultLevel = FANCY; //put your default here
}
then define module like this:
public class DefaultLevelModule extends AbstractModule
{
#Override public void configure()
{
bind(Level.class).toInstance(Level.defaultLevel);
}
}
It's the issue 295 and looks like a very trivial bug.
I've patched it for myself and maybe one day somebody there will fix this very old issue, too.

Categories

Resources