Josh,

Well, let's see some official opinion on this matter, i'm just a user of
guice.

--
L

On Sun, Feb 1, 2015 at 8:20 PM, Joshua Moore-Oliva <[email protected]>
wrote:

> Thanks, that helped me ramp up on the guice ecosystem and configuration.
>
> However - the fact that setDefault has different binding coercion
> semantics to setBinding smells like a bug to me. Is there any reason that
> its current behaviour is the correct one?  If not I might file a bug report.
>
>
>
>
> On Saturday, January 31, 2015 at 4:42:38 AM UTC-7, Laszlo Ferenczi wrote:
>>
>> Hi Josh,
>>
>> Using the binding mechanism in guice might be a bit of an overkill for
>> simple property bindings, however you have a number of alternatives:
>>
>> - Properties itself allows for defaults handling, create a property file
>> with the defaults and create one for the application and merge them in the
>> properties object. Binding the resulting properties will have the defaults.
>> - Bind the properties object itself and manually fetch the values from it
>> - Specify defaults in the code, only override when you get a meaningful
>> value injected
>>
>> A more complex way would be to roll your own configuration management.
>> In our projects we use an in-house developed ConfigManager object which
>> is similar to the Properties object but it provides typesafe getters for
>> various primitive types. A generic TypeListener allows the injections of
>> config values into the object. (conceptually very similar of what netflix's
>> governator does)
>> Obviously you can use the governator library also, it'll do the trick
>> (and many others). I believe apache onami has a solution for configuration
>> too.
>>
>> Hope this helps.
>>
>>
>> --
>> L
>>
>> On Sat, Jan 31, 2015 at 5:25 AM, Joshua Moore-Oliva <[email protected]>
>> wrote:
>>
>>> First off - I am only a few days into guice, so I apologize if my
>>> questions are dumb - I have tried to compensate with what I hope are clear
>>> examples.
>>>
>>> I am attempting to make liberal use of OptionalBinder in my code to
>>> allow some default values to be specified and then potentially overriden by
>>> configuration. I have run into a cascading series of issues. First, lets
>>> start with what works in a simple case that currently works (but does not
>>> have the default value functionality I hope to achieve with OptionalBinder):
>>>
>>> public class Simple {
>>>     private final int simpleInt;
>>>     private final String simpleStr;
>>>
>>>     @Inject
>>>     public Simple(@Named("simpleInt") int simpleInt, @Named("simpleStr")
>>> String simpleStr) {
>>>         this.simpleInt = simpleInt;
>>>         this.simpleStr = simpleStr;
>>>     }
>>>
>>>     public int getSimpleInt() {
>>>         return simpleInt;
>>>     }
>>>
>>>     public String getSimpleStr() {
>>>         return simpleStr;
>>>     }
>>> }
>>>
>>>
>>> If I wish to wire up the value of simpleVal from configuration, I can
>>> use Names.bindProperties (which was recommended in the FAQ)
>>>
>>> public class SimpleTest {
>>>
>>>     @Test
>>>     public void testGetSimpleVal() throws Exception {
>>>         Injector inj = Guice.createInjector(getTestModule());
>>>         Simple s = inj.getInstance(Simple.class);
>>>
>>>         assertEquals(1, s.getSimpleInt());
>>>         assertEquals("default", s.getSimpleStr());
>>>     }
>>>
>>>     private Module getTestModule() {
>>>         return new AbstractModule() {
>>>             @Override protected void configure() {
>>>                 bind(Simple.class);
>>>
>>>                 Properties p = new Properties();
>>>                 p.setProperty("simpleInt", "1");
>>>                 p.setProperty("simpleStr", "default");
>>>
>>>                 Names.bindProperties(binder(), p);
>>>             }
>>>         };
>>>     }
>>> }
>>>
>>> So far, so good - Names.bindProperties is great, it even converts my
>>> strings to ints ("1" -> 1 for simpleInt)
>>>
>>> Now I attempt to set up default values for simpleInt and simpleStr using
>>> OptionalBinder. I do this by returning another Module with default
>>> bindings. (I assume I should do this on a package not class level, but it
>>> keeps the example simple).
>>>
>>> public class Simple {
>>>     private final int simpleInt;
>>>     private final String simpleStr;
>>>
>>>     @Inject
>>>     public Simple(@Named("simpleInt") int simpleInt, @Named("simpleStr")
>>> String simpleStr) {
>>>         this.simpleInt = simpleInt;
>>>         this.simpleStr = simpleStr;
>>>     }
>>>
>>>     public int getSimpleInt() {
>>>         return simpleInt;
>>>     }
>>>
>>>     public String getSimpleStr() {
>>>         return simpleStr;
>>>     }
>>>
>>>     public static AbstractModule getDefaultsModule() {
>>>         return new AbstractModule() {
>>>             @Override protected void configure() {
>>>
>>>                  OptionalBinder.newOptionalBinder(binder(),
>>> Key.get(Integer.class, Names.named("simpleInt" )))
>>>                         .setDefault().toInstance(12345);
>>>
>>>                 //OptionalBinder.newOptionalBinder(binder(),
>>> Key.get(String.class, Names.named("simpleInt" )))
>>>                 //        .setDefault().toInstance("12345");
>>>
>>>                 OptionalBinder.newOptionalBinder(binder(),
>>> Key.get(String.class, Names.named("simpleStr")))
>>>                         .setDefault().toInstance("Simple class
>>> default");
>>>             }
>>>         };
>>>     }
>>> }
>>>
>>> public class SimpleTest {
>>>
>>>     @Test
>>>     public void testGetSimpleVal() throws Exception {
>>>         Injector inj = Guice.createInjector(Simple.getDefaultsModule(),
>>> getTestModule());
>>>         Simple s = inj.getInstance(Simple.class);
>>>
>>>         assertEquals(1, s.getSimpleInt());
>>>         assertEquals("default", s.getSimpleStr());
>>>     }
>>>
>>>     private Module getTestModule() {
>>>         return new AbstractModule() {
>>>             @Override protected void configure() {
>>>                 bind(Simple.class);
>>>
>>>                 Properties p = new Properties();
>>>                 p.setProperty("simpleInt", "1");
>>>                 p.setProperty("simpleStr", "default");
>>>
>>>                 Names.bindProperties(binder(), p);
>>>             }
>>>         };
>>>     }
>>> }
>>>
>>> At this point, I get the following error
>>>
>>> 1) A binding to java.lang.String annotated with
>>> @com.google.inject.name.Named(value=simpleStr) was already configured
>>> at com.baselayer.device.scratch.Simple$1.configure(Simple.java:42).
>>>   at com.baselayer.device.scratch.SimpleTest$1.configure(
>>> SimpleTest.java:33)
>>>
>>> Looking into the source code for Names.bindProperties, I see this is
>>> because it does not use setBinding, but rather normal non-optional
>>> .toInstance.
>>>
>>> binder.bind(Key.get(String.class, new NamedImpl(propertyName))).toIn
>>> stance(value);
>>>
>>> At this point, I assume there is some good reason that you must use
>>> setBindings, though I will make the suggestion that as a new user of guice,
>>> I would expect a normal non-optional .toInstance to override a .setDefault.
>>>
>>> So, shamelessly copying the source code of Names.bindProperties, I
>>> created my own replacement test module as follows:
>>>
>>>     private Module getTestModule() {
>>>         return new AbstractModule() {
>>>             @Override protected void configure() {
>>>                 bind(Simple.class);
>>>
>>>                 Properties p = new Properties();
>>>                 p.setProperty("simpleInt", "1");
>>>                 p.setProperty("simpleStr", "default");
>>>
>>>                 //Names.bindProperties(binder(), p);
>>>                 //The following code replaces Names.bindProperties
>>>                 Binder skippedBinder = binder().skipSources(Names.
>>> class);
>>>
>>>                 for (Enumeration<?> e = p.propertyNames();
>>> e.hasMoreElements(); ) {
>>>                     String propertyName = (String) e.nextElement();
>>>                     String value = p.getProperty(propertyName);
>>>                     OptionalBinder.newOptionalBinder(skippedBinder,
>>> Key.get(String.class, Names.named(propertyName))).
>>> setBinding().toInstance(value);
>>>                 }
>>>             }
>>>         };
>>>     }
>>>
>>> However - unlike Names.bindProperties, the String binding will not stick
>>> to simpleInt.  The very useful auto-mutation of String to Int that occurs
>>> with non-optional .toInstance does not appear with .setBinding. Either I
>>> get
>>>
>>> 1) No implementation for java.lang.Integer annotated with
>>> @com.google.inject.name.Named(value=simpleInt) was bound.
>>>   while locating java.lang.Integer annotated with
>>> @com.google.inject.name.Named(value=simpleInt)
>>>     for parameter 0 at com.baselayer.device.scratch.
>>> Simple.<init>(Simple.java:20)
>>>   at com.baselayer.device.scratch.SimpleTest$1.configure(
>>> SimpleTest.java:27)
>>>
>>>
>>> If I do not have any .setDefault bound for simpleInt in
>>> Simple.getDefaultsModule (neither Integer.class or String.class), or I get
>>>
>>> Failed tests:   testGetSimpleVal(com.baselayer.device.scratch.SimpleTest):
>>> expected:<1> but was:<12345>
>>>
>>> If I had bound an Integer in Simple.getDefaultsModule with
>>>
>>> OptionalBinder.newOptionalBinder(binder(), Key.get(Integer.class,
>>> Names.named("simpleInt" )))
>>>                         .setDefault().toInstance(12345);
>>>
>>> Finally - If I use
>>>
>>> OptionalBinder.newOptionalBinder(binder(), Key.get(String.class,
>>> Names.named("simpleInt" )))
>>>                         .setDefault().toInstance("12345");
>>>
>>> But do NOT overwrite it with setBinding(), then I get the following error
>>>
>>> 1) No implementation for java.lang.Integer annotated with
>>> @com.google.inject.name.Named(value=simpleInt) was bound.
>>>   while locating java.lang.Integer annotated with
>>> @com.google.inject.name.Named(value=simpleInt)
>>>     for parameter 0 at com.baselayer.device.scratch.
>>> Simple.<init>(Simple.java:20)
>>>   at com.baselayer.device.scratch.SimpleTest$1.configure(SimpleTest.java:30)
>>> (via modules: com.google.inject.util.Modules$OverrideModule ->
>>> com.baselayer.device.scratch.SimpleTest$1)
>>>
>>>
>>> It appears that guice will bind a String to an int with the normal,
>>> non-optional .toInstance, but .setDefault  .toInstance refuses to bind a
>>> String to an int with the OptionalBinder. (but .setBinding .toInstance will
>>> set a String to an Integer, as long as a separate specific Integer binding
>>> does not already exist).
>>>
>>> If .setDefault and .setBinding .toInstance had the same semantics as
>>> normal, non-optional .toInstance, I could make my own Names.bindProperties
>>> and use that everywhere instead (with the intent being that if any default
>>> was already set, I would overwrite it)
>>>
>>> However, the only thing I have tried that works is to attempt to bind to
>>> every single instance (ensuring that the exact type has been bound),
>>> something like this for getTestModule()
>>>
>>>     private Module getTestModule() {
>>>         return new AbstractModule() {
>>>             @Override protected void configure() {
>>>                 bind(Simple.class);
>>>
>>>                 Properties p = new Properties();
>>>                 p.setProperty("simpleInt", "1");
>>>                 p.setProperty("simpleStr", "default");
>>>
>>>                 //Names.bindProperties(binder(), p);
>>>                 Binder skippedBinder = binder().skipSources(Names.
>>> class);
>>>
>>>                 for (Enumeration<?> e = p.propertyNames();
>>> e.hasMoreElements(); ) {
>>>                     String propertyName = (String) e.nextElement();
>>>                     String value = p.getProperty(propertyName);
>>>                     OptionalBinder.newOptionalBinder(skippedBinder,
>>> Key.get(String.class, Names.named(propertyName))).
>>> setBinding().toInstance(value);
>>>
>>>                     try {
>>>                         int i = Integer.parseInt(value);
>>>                         OptionalBinder.newOptionalBinder(skippedBinder,
>>> Key.get(Integer.class, Names.named(propertyName))).
>>> setBinding().toInstance(i);
>>>                     } catch (NumberFormatException asdf) {
>>>                     }
>>>                 }
>>>             }
>>>         };
>>>     }
>>>
>>> In order to bind a configuration file's properties to @Named instances,
>>> I need to attempt to convert the String to each possible type if I wish to
>>> load dynamically without some kind of type metadata in the configuration
>>> file as well to know which type I should target the binding to.
>>>
>>> Having outlined all this, a few questions
>>>
>>> 1) Is it feasible for the behaviour of OptionalBinder to allow a
>>> .toInstance to override a .setDefault? If so then things like
>>> Names.bindProperties could (maybe) just work as is. If not, why? (I assume
>>> there is a good reason since .to was explicitly disallowed as an override
>>> for .setDefault in the documentation)
>>>
>>> 2) What is the intended behaviour of Names.bindProperties.  Are strings
>>> supposed to be able to inject onto a Integer? Or only inject if a specific
>>> Integer injection doesn't already exist? (Are users supposed to be able to
>>> inject different String and Integer toInstance values for a Named
>>> annotation?)  Is this behaviour I am experiencing via .setDefault something
>>> that hasn't been fully thought through, or is there a ruleset for when some
>>> types are supposed to ovverride other types when a more specific mapping
>>> doesn't already exist for the annotation?
>>>         Experimenting with Modules.override it seems that normal
>>> non-optional .toInstance starts behaving similarly if the defaults module
>>> binds both an Integer and a String, and the overriding module just binds a
>>> string (As Names.bindProperties).
>>>
>>> 3) Are there any downsides to just OptionalBinding any configuration
>>> file property to a variety of common primitive types to ensure that all
>>> default injection parameters gets overridden by configuration?
>>>
>>> 4) If I just want default values (without the Optional<Foo> support),
>>> should I be using Modules.override instead?
>>>
>>> 5) Is setDefault working as intended, given that is has different
>>> semantics to .setBinding (.setDefault .toInstance String.class will not
>>> inject a String to an Integer in the absence of a Integer injection, but
>>> .setBinding .toInstance will just like normal non-optional .toInstance)
>>>
>>> Thanks for reading if you made it this far in my rocky start to DI in
>>> java :)
>>>
>>> Josh
>>>
>>>  --
>>> You received this message because you are subscribed to the Google
>>> Groups "google-guice" group.
>>> To unsubscribe from this group and stop receiving emails from it, send
>>> an email to [email protected].
>>> To post to this group, send email to [email protected].
>>> Visit this group at http://groups.google.com/group/google-guice.
>>> To view this discussion on the web visit https://groups.google.com/d/
>>> msgid/google-guice/0c3bb455-d65d-4ed4-bf7d-e84d5f84f7af%
>>> 40googlegroups.com
>>> <https://groups.google.com/d/msgid/google-guice/0c3bb455-d65d-4ed4-bf7d-e84d5f84f7af%40googlegroups.com?utm_medium=email&utm_source=footer>
>>> .
>>> For more options, visit https://groups.google.com/d/optout.
>>>
>>
>>  --
> You received this message because you are subscribed to the Google Groups
> "google-guice" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To post to this group, send email to [email protected].
> Visit this group at http://groups.google.com/group/google-guice.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/google-guice/a88525ad-a952-4364-9e30-d6bce7ea173a%40googlegroups.com
> <https://groups.google.com/d/msgid/google-guice/a88525ad-a952-4364-9e30-d6bce7ea173a%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
>
> For more options, visit https://groups.google.com/d/optout.
>

-- 
You received this message because you are subscribed to the Google Groups 
"google-guice" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/google-guice.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/google-guice/CAD-udUBx0-UCyXwfYbL0R4_FYrzdoR7YK81_vD%2BHvXsLwDe9tw%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to