Closed rmannibucau closed 4 years ago
Do you mean you defined
myConfig=
in config source, you will get empty string instead of null. Do you want to get null instead? If not, can you elaborate your use case?
I'm not sure about myConfig=[nothing] case but more than myConfig doesn't exists and cdi bean uses the snippet I put in the first message, by default I expect it to be null instead of fail (required by tck today). I don't want (also can't in several cases) inject Optional
Something like @ConfigProperty(name="...", nullAllowed=true) would be nice.
or optional=true
sounds weird with Optional class no: "optional=true will set null and you can inject Optional
-1 on optional=true, as it contradicts with the contract of "the presence of defaultValue" means optional.
How about to add a new constant on ConfigProperty?
String NULL_VALUE="org.eclipse.microprofile.config.configproperty.nullvalue";
Usage:
@Inject
@ConfigProperty(name="..." defaultValue=ConfigProperty.NULL_VALUE)
String value;
I had that in mind as well so +1
The currently proposed solution does not seem correct. One alternative discussed in #512 was to add a separate nullable
annotation property indicating that the injected field should be treated as if it were optional with an orElse(null)
qualification. This property would not be allowed to be true
if the injection target is not nullable (that is, it is primitive).
+1
Hmm, why isnt it correct? Having a flag or a flag is the same. One is likely more elegant than the other but both are strictly equivalent. Now, for the primitive case, you must consider the magic string NULL_VALUE means "the configured value is null but passthrough" so the output takes its default. Overall point is that duplicating this notion just adds noise to the spec. Note, however, this is already outside the spec - like geronimo proxy support - since in practise using a default of NULL_VALUE is only valid in cdi injections so not primitives so we are safe anyway.
Hmm, why isnt it correct? Having a flag or a flag is the same. One is likely more elegant than the other but both are strictly equivalent.
They are not strictly equivalent. How would you then specify the default value for a nullable property? If default values are implemented using a low priority configuration source (as we do in Quarkus), how do you represent this special behavior?
Bear in mind that we already have a recommended approach for optional values, and that is Optional
.
Now, for the primitive case, you must consider the magic string NULL_VALUE means "the configured value is null but passthrough" so the output takes its default.
That is a bit of a stretch, I'd say. Anyway I believe I already proposed in #476 that primitive types should already implicitly default to their Java default values, which I think is probably a cleaner approach. It doesn't make these properties optional, but it does remove boilerplate.
Note, however, this is already outside the spec - like geronimo proxy support - since in practise using a default of NULL_VALUE is only valid in cdi injections so not primitives so we are safe anyway.
I'm not sure what you mean by this.
How would you then specify the default value for a nullable property?
This is the whole point, you can't be both at the same time. If you have a default then you are not nullable so at the end it is just about requesting to accept null as a default. The NULL_VALUE (mp-config 1.4) is exactly there for this case.
Bear in mind that we already have a recommended approach for optional values, and that is Optional.
As explained before it goes partially against CDI and fully against java recommendation so it can't last IMHO.
The last part which was likely unclear is that the coercing of defaults for primitive is not needed in the spec, there is no way to inject a primitive with CDI so no need to spec how to do it. The only existing case it is useful is the case the impl supports configuration proxies (see an example here https://github.com/apache/geronimo-config/blob/trunk/impl/src/test/java/org/apache/geronimo/config/test/internal/ProxyTest.java#L104) but it is out of the spec for now.
How would you then specify the default value for a nullable property?
This is the whole point, you can't be both at the same time. If you have a default then you are not nullable so at the end it is just about requesting to accept null as a default. The NULL_VALUE (mp-config 1.4) is exactly there for this case.
This is factually incorrect. Users can have a property which is optional/emptiable but also has a default value. If they want to explicitly clear the property, they may do so. At least, this works fine on SmallRye/Quarkus.
Bear in mind that we already have a recommended approach for optional values, and that is Optional.
As explained before it goes partially against CDI and fully against java recommendation so it can't last IMHO.
This should be tackled in a separate issue, since Optional
is used extensively by this API for this purpose. Using this issue as a proxy for that discussion is counter-productive.
The last part which was likely unclear is that the coercing of defaults for primitive is not needed in the spec, there is no way to inject a primitive with CDI so no need to spec how to do it. The only existing case it is useful is the case the impl supports configuration proxies (see an example here https://github.com/apache/geronimo-config/blob/trunk/impl/src/test/java/org/apache/geronimo/config/test/internal/ProxyTest.java#L104) but it is out of the spec for now.
OK.
Users can have a property which is optional/emptiable but also has a default value.
No, the meaning of defaultValue is "use that if not set". There is no way to set null anywhere in the configuration (getValue in a ConfigSource does not return an optional so no way to know it is null or not set at all). There is also no reason to set a default value if you support null. So at the end both cases are exactly the same: what value do you use if you get null from config sources.
What you are speaking about is another topic: how to specify null when there is a default value. In other words it is how to set explicitly null in/from the config source. This is another topic for the "core" API, not the CDI integration which uses NULL_VALUE as a marker in the @ConfigProperty annotation - as unconfigured marker which is not supported in the getValue/getOptionalValue methods. I don't think we want to use the constants of @ConfigProperty in Config to use them as marker + in the core API this distinction is not needed because you can set a default using getOptionalValue().orElse as easily as having a marker. So at the end, the injection API is covered now, and the standalone one too IMHO.
Using this issue as a proxy for that discussion is counter-productive.
Not sure I followed this one. Factually Optional fields do not work in passivable CDI beans so it prevents to use the spec and it is where this issue is coming from.
There is no way to set null anywhere in the configuration (getValue in a ConfigSource does not return an optional so no way to know it is null or not set at all).
This issue has been raised separately and needs to be resolved, IMHO. Personally, I would like to see the empty string ""
used to indicate the presence of a property with an absent value in a config source, as distinct from a null
which means the property is not present and other sources should be considered.
The last part which was likely unclear is that the coercing of defaults for primitive is not needed in the spec, there is no way to inject a primitive with CDI so no need to spec how to do it.
Primitives types are valid beans and injectable per CDI. CDI also specifies that null
gets coerced to a primitive's default at injection time. It's true they are not proxyable, but that should not be a concern for MicroProfile Config.
The debate about how to indicate nullability in the annotation is largely driving by limitations in the expressiveness of annotation structures. I think the guiding principle here is that we need to avoid further complicating the interpretation of value strings by overloading the use of the defaultValue
annotation field to convey addition meaning.
It can certainly be argued that this is ambiguous at a glance:
@ConfigProperty(name = "foo", nullable = true, defaultValue = "bar")
String foo;
But so is this:
@ConfigProperty(name = "foo", defaultValue = "bar")
Optional<String> foo;
It is easy enough to specify that defaultValue
must be empty when nullable
is true
, and CDI gives us a convenient way to detect that condition at bootstrap time and provide meaningful feedback.
Maybe the real issue here is the defaultValue
support itself. Config
doesn't have
<T> T getValue(String propertyName, String defaultValue, Class<T> propertyType);
So why should we provide such convenience during injection? As recommended in the javadocs, a developer is almost certainly better off simple bundling the default values in META-INF/microprofile-config.properties
.
The debate about how to indicate nullability in the annotation is largely driving by limitations in the expressiveness of annotation structures. I think the guiding principle here is that we need to avoid further complicating the interpretation of value strings by overloading the use of the
defaultValue
annotation field to convey addition meaning.
+1 here
It can certainly be argued that this is ambiguous at a glance:
@ConfigProperty(name = "foo", nullable = true, defaultValue = "bar") String foo;
But so is this:
@ConfigProperty(name = "foo", defaultValue = "bar") Optional<String> foo;
It is easy enough to specify that
defaultValue
must be empty whennullable
istrue
, and CDI gives us a convenient way to detect that condition at bootstrap time and provide meaningful feedback.
We support this case. Having a default is not the same as making a property be required (if you assume the reverse, all kinds of ambiguous situations appear, as previously discussed).
Maybe the real issue here is the
defaultValue
support itself.Config
doesn't have<T> T getValue(String propertyName, String defaultValue, Class<T> propertyType);
This has been proposed. But using a config source is more future-proof (calling back to the property expansion discussion).
@emattheis the default value is handled in the programmatic API through getOptionalValue. Being programmatic, the dev is responsible for making it efficient and well implemented. Being said the mainstream way to get injections is fields, it must already resolve the defaults for performances reasons. It is also inspired from the 3-4 implementations which created MP-config (deltaspike, sabot and friends) which all ended up doing that cause it was the mainstream case.
@emattheis the default value is handled in the programmatic API through getOptionalValue.
This is just not generally true.
Having a default is not the same as making a property be required (if you assume the reverse, all kinds of ambiguous situations appear, as previously discussed).
Seems like there are just three cases to worry about for injection:
null
or Optional
This has been proposed. But using a config source is more future-proof (calling back to the property expansion discussion).
Agree. I am certainly not advocating for its introduction, just arguing for @ConfigProperty
to follow semantically with the Config
API.
the default value is handled in the programmatic API through getOptionalValue. Being programmatic, the dev is responsible for making it efficient and well implemented.
Precisely my point. The same option is available post-injection in CDI.
It is also inspired from the 3-4 implementations which created MP-config (deltaspike, sabot and friends) which all ended up doing that cause it was the mainstream case.
Sometimes following the leader isn't the best way to go 😛. IMHO, hardcoded defaults in source code is an anti-pattern to be avoided.
Well it is key to have a standard way to set the default and dont rely on a coded one. It normalizes the config interaction, to understand the config without digging into any code and enables to generate doc trivially. It would be a huge regression to be programmatic only and would make the injection almost useless and rewrapped in a custom api as before it exists (if apps dont move back to deltaspike).
It would be a huge regression to be programmatic only and would make the injection almost useless and rewrapped in a custom api as before it exists (if apps dont move back to deltaspike).
Do you actually have data to support this claim? This is a common theme that comes up around breaking changes. It's tempting to look at an existing API and say that any breaking change would be a large impact, but that assumes users are heavily invested in the feature you are breaking. I'm genuinely curious how we gauge the usage of features and weigh the impact of breaking changes.
That being said, if we removed defaultValue
from the annotation, anyone using it would simply need to move the value to META-INF/microprofile-config.properties
. I'm not sure how that would make injection "almost useless" or compel someone to move back to deltaspike.
This issue is purely about allowing users to specify that they want a property to be optional without using an explicit Optional
.
So the only questions that matter are:
nullable
property cannot be supported?false
?IMO
Should we allow users to do this at all? Yes
Is there any reason that a boolean
nullable
property cannot be supported? NoIs there any problem with making the default value of this property be
false
? No
No @dmlloyd, the issue is about enabling null to be injected - as you said - but not to duplicate an existing api. Note that I think NULL_VALUE solves that already so this issue is fixed I guess.
Side note: on your question about how do we gauge the usage: i guess we all use our own experience. On my side note a single app wpuld work dropping that feature. Moving to a properties file require new tooling too (for fatjar and living doc for example to cite just 2).
Note that I think NULL_VALUE solves that already so this issue is fixed I guess.
Only on an unreleased version that is now receiving scrutiny by implementors since we're at RC-2.
My apologies for muddying the waters with the defaultValue
discussion. @rmannibucau are you firmly opposed to the nullable
field instead of the sentinel value?
Hmm RC1 - not a beta - is released and has NULL_VALUE so guess game is kind of played for this year.
I'm strongly against duplicating any information and/or making it ambiguous.
In other words, nullable=true would be ok if NULL_VALUE is removed since then chain flow is unique (if default is configured else if nullable else fail) IMHO.
I think this is the proposal: replace NULL_VALUE with nullable = true.
RC1 and RC2 are final releases (RC are not supposed to have breaking changes) so I'm -1 (as an user so not strictly blocking). Microprofile already has a bad image cause of the breaking changes it does so guess we should learn to limit it as much as possible, in particular when it is just about semantic and not feature related.
In other words, nullable=true would be ok if NULL_VALUE is removed since then chain flow is unique (if default is configured else if nullable else fail) IMHO.
I think we’re in agreement there. @dmlloyd ’s request was specifically to revert the NULL_VALUE
work, so we would replace that with nullable
property.
As far as RC semantics go, I was not aware of any alpha or beta release - where does one evaluate those?
@rmannibucau is there a definition that precludes a feature that's present in an RC from being removed in a subsequent RC?
My understanding was that an RC is a possible release candidate for the specification, not a final release. If in evaluating the RC it becomes apparent that a feature has a problem, I don't see it being an issue to remove or adjust a feature between RC releases if necessary.
Seems RC is not very well defined (I'm using https://wiki.eclipse.org/MicroProfile/SpecRelease/Release as a ref). My understanding, and how I interpret it is:
Overall point is that implementors can always use snapshots to evaluate API changes so guess this definition is fine but also agree it can need to be made more explicit (likely another ticket ;)).
Yeah, it was raised the other day that we haven't voted on spec releases in MP for ages. I think it's been put on hold until the Working Group decision is made because the spec process there would define the voting.
For reference, I recall at least one specification in the past that made breaking changes between RC releases due to issues that came about in verifying an RC.
Hmm, I wouldnt say that because it had been done it should be redo.
Let's try another approach: is the 1.4 needed? Can't we go 2.0 directly adding a few other tickets (maybe proxying API to cite one)? There the breaking change would be less an issue I guess. Alternatively it can go in 2.0.
Just to highlight a few points: having specs having breaking changes breaks the full ecosystem in several environments, OSGi is one coming to my mind but it is not the only one. This shouldn't be underestimated because the immediate linking we are thinking about is the classpath because it requires a lot of effort after to catch up for apps (I ignore here the vendor effort but it is real too).
This one was blocking a few things for implementors: https://github.com/eclipse/microprofile-config/issues/390
BTW, if there is no 1.4, there is no breaking change.
There are 2 1.4 - RC is when it starts to be consummed, beta and alpha are generally ignored. Guess you (= eclipse members) can access the stats on central so see how much it got already consumed to judge more precisely.
I don't agree that it's a breaking change to alter a feature that was only present in the master
branch previously. My understanding is that the Null behavior wasn't present in 1.3, so whether it is or isn't ultimately in 1.4 it doesn't impact 1.4 being a non-breaking change release.
Are you saying that once an RC is cut there is absolutely no opportunity to say a mistake was made and a feature needs to change? If so, that's not how MicroProfile specifications have been working to date
Even if this is the case, the RC was cut a couple of days ago. To my knowledge, there is no release from any implementor with this version yet, so no end users using it right now.
Also, RC1 and RC2 came on the same day, with nothing prior to that since May 2018 🤔 If there is a plan for alpha or beta releases, I'd love to know.
@rmannibucau I can appreciate your frustration since it looks like you filed this back when 1.3 was released. Personally, I wasn't aware of it until recently.
Well, this thread wouldnt have popped out, apache would be releasing a 1.4 version. It is on hold cause of this - and note there is no consumer issue with the snapshot and this issue is cleanly solved too (updated 2 apps with no issue but all null workarounds removed).
If RC are not RC then please 1. call them alpha to remove any ambiguity and add a warning for users that it is unstable and must not be used (what a SNAPSHOT is semantically) 2. write a blog post about it to clearly spread the work cause it is not what RC are commonly.
It seems premature for an implementation to release a version based on a release candidate of the spec. We can argue about what sort of changes can be made during a RC cycle, but the whole point is to have the chance to make them, right?
Hmm, dont you mix milestones where, I agree, goal is to test, and RC where goal is to do a final but due to immutable state of releases RC are used in case something unexpectedly bad is found (and then final is just a copy of the first "successful RC") - can be a bad manifest, bad bytecode version and so on, but not a design issue?
It might be that we need to adjust the spec release process to actually do a Beta first to indicate "feature complete" but not candidate release.
In the past, and present, that's been problematic as releases tend to be right before an MP platform release, but it's something we should look at
Anyway, are we reaching a conclusion here? Are we all in agreement to rewrite the feature like @dmlloyd suggested?
If all actions are taken at the same time (code change but more importantly making it explicit through the process doc page what RC should be - I don't care you qualify them as "beta" or decide to keep the RC semantic and to do milestones instead + writing somewhere that both RC are "beta" - I let native speakers find more appropriated words) no issue on my side to revert/add nullable.
I think that is reasonable. @Emily-Jiang what do you think?
I finally caught up with the conversation.
As for whether we can make more changes in RCx, yes, we can. In MicroProfile process, we use RC as the experiment for releases. We can have multiple RCs before a final release is cut. Maybe we should align with Jakarta EE process under discussion - Mx - RCx
as for the issue of specifying null in the default source. If the property does not exist in the configsource, it will be null. We can branch off again to discuss how to represent null.
as for nullable = true
, I think it should work. The default value for nullable set to false.
I thought about this further: Since we have two ways to specify default value now, we need to either
A: disallow the coexist of defaultValue and null
B: specify defaultValue has a higher priority over nullable.
@Inject @ConfigProperty(name="someProp", defaultValue="me", nullable=true) String somePropVaue
Thoughts?
It must be allowed to inject that and get null, a workaround is to use an optional but it is not recommanded (even the opposite) to use optionals as fields so this is not a solution. Injecting null is fine from all point of views and compatible with mainstream config solution so it should probably make its path in mp-config asap.