-
Notifications
You must be signed in to change notification settings - Fork 116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ouf of date: ConfigValue implementation for dynamic properties #91
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't merge the dynamic changes handling in. I'll do a pr for my solution. We can then compare next week.
|
||
Assert.assertEquals(config.access("tck.config.test.javaconfig.configvalue.boolean.oui").as(Boolean.class).get(), Boolean.TRUE); | ||
Assert.assertEquals(config.access("tck.config.test.javaconfig.configvalue.boolean.oui_uppercase").as(Boolean.class).get(), Boolean.TRUE); | ||
Assert.assertEquals(config.access("tck.config.test.javaconfig.configvalue.boolean.oui_mixedcase").as(Boolean.class).get(), Boolean.TRUE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really like to force to convert y, ja, j n, oui to true. It is not well known. I will suggestion to convert yes, y, on to true, which should be sufficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't speak french? :)
Seriously, we have seen this as requests in DeltaSpike. And it doesn't hurt.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't quote DeltaSpike. We are not defining specs around DeltaSpile.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is fine for DeltaSpike to expand this. -1 on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with Emily, the specification should be formal and pick only widely used options. Any implementation can support more options, and if it doesn't a custom converter should solve the problem. Otherwise, we would have to support all language variants - possibly Japanese, since Fujitsu have also joined MP.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok for me, we can move this to be an implementation detail.
Just let me know which valid list of 'true' values you like to support out of the box.
interface ConfigChanged { | ||
<T> void onValueChange(String key, T oldValue, T newValue); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not what we discussed ConfigValue. What we discussed is just a proxy. This forces to configure the pulling intervals per property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, this is exactly what we discussed originally. Without any optional caching you will trash the config system (as explained on the hangout yesterday).
This is not a feedback from 'working sample' but from years in production:
https://github.com/apache/deltaspike/blob/master/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/ConfigResolver.java#L681
But you can also simply not use cacheFor then it will go directly to the underlying Config.
And this features is of course independent per config value. Because for some config values it's fine to reload them every 15 minutes while others must be refreshed all few seconds. This is really highly depending on the business meaning behind each value.
The next task is to show how we can leverage this for CDI injection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After we decided that we would injection Provider instead of ConfigValue proxy and closing #42, ConfigValue class can be used for a different purpose, as described in this PR. It is now not intended to be injected into CDI beans, but in JavaSE API to allow for programmatic configuration and access to metadata of a single config value. In CDI API, this should be done via annotations on an injection point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that ConfigValue in this PR covers too many things at once. I'd like to split some methods into a separate PR. As a base, I think it is a no-brainer to leave as
, get
, getOptional
, getKey
and move other methods into PRs on their own. We can then discuss separately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The get has been provided by Provider. I don't see any more value added by this class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@struberg The title of this PR is "ConfigValue implementation for dynamic properties" and regarding this, it doesn't offer any more value than a Provider we already accepted to be used.
The other features are not related much to the title of this PR and I would separate them to other PRs, a PR per feature, because they are mostly orthogonal. Would you agree to split it to more PRs, so that we may merge the migration of TCK tests to arquillian and further discuss the new features added by ConfigValue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@OndrejM it adds a huge additional value indeed.
Currently you can inject a Long or a Provider<Long>.
A native (or wrapper) type provides no refresh at all. Using Provider will always go to the underlying Config system.
the cacheFor
of ConfigValue otoh provides means to locally cache values and prevents going to the underlying Config system for a specified amount of time. So it effectively removes tons of load from the Config system which gives the Config itself more room to do other tasks. It really heavily reduces the overall load.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not saying it doesn't add any value. But I'd like to get the arquillian migration in TCK merged before we come to any conclusion about ConfigValue...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@OndrejM and @Emily-Jiang, I separated the test setup into an own commit for #107
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @struberg! Now we can continue in this lengthy discussion :)
Signed-off-by: Mark Struberg <[email protected]>
Signed-off-by: Mark Struberg <[email protected]>
Signed-off-by: Mark Struberg <[email protected]>
hacked together with @OndrejM Signed-off-by: Mark Struberg <[email protected]>
places resources at correct location in the ShrinkWrap archive We cannot keep them 1:1 in the same location as they would otherwise get picked up twice in locale Arquillian containers (getResources). Signed-off-by: Mark Struberg <[email protected]>
* moving away the resources from the standard classpath * moving from a JAR to a WebArchive * bundling all resources into the WebArchive Signed-off-by: Mark Struberg <[email protected]>
Signed-off-by: Mark Struberg <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to split the ideas demonstrated in ConfigValue class into separate PRs and leave only basic stuff and migration to arquillian test cases so that we can merge it. Or please a separate PR for migration to arquillian, so that we can discuss proposed changes further.
interface ConfigChanged { | ||
<T> void onValueChange(String key, T oldValue, T newValue); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After we decided that we would injection Provider instead of ConfigValue proxy and closing #42, ConfigValue class can be used for a different purpose, as described in this PR. It is now not intended to be injected into CDI beans, but in JavaSE API to allow for programmatic configuration and access to metadata of a single config value. In CDI API, this should be done via annotations on an injection point.
* | ||
* @return This builder | ||
*/ | ||
ConfigValue<T> cacheFor(long value, TimeUnit timeUnit); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand that this method allows defining a business requirement to cache a value for at most value+timeUnit period. This is to prevent longer caching for sensitive data, like blacklists, which should be applied ASAP. However, I'm not sure this can be ensured if config sources implement internal caching. Is there a way to notify all config sources that this value should not be cached for more than configured interval?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would not want to specify this on the property basis. It adds too much burden on the developers. Surely, the cache for has no effect if the underline config sources have no changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is often a business requirement. Different config properties often have different requirements for being up to date. This is something we added to DeltaSpike as a result to business needs. So there IS a real world need to handle this on a per-attribute basis.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, I'm not sure this can be ensured if config sources implement internal caching.
The caching of the ConfigValue is totally independent from any possible caching of any ConfigSource. This is really a local cache for a single attribute evaluation inside each ConfigValue instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@OndrejM in addition to my last reply: One of the points of having the ability to cache on a per-property basis is that this takes a lot load away from the Config system itself. So the ConfigSources usually are able to implement updates much faster because they have generally less load.
* @param evaluateVariables whether to evaluate variables in values or not | ||
* @return This builder | ||
*/ | ||
ConfigValue<T> evaluateVariables(boolean evaluateVariables); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also clarify the default value if evaluateVariables is not set
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you mean if the referenced variable is not found?
Yes, that's a good point which should get clarified!
In that case we better leave the original string untouched.
* | ||
* @return This builder | ||
*/ | ||
ConfigValue<T> onChange(ConfigChanged valueChange); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have discussed this - it is not clear in which context (thread) the ConfigChanged lambda would be executed. I would drop this method until we clarify this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are we going to achieve here? A use case will help.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@OndrejM it will be a synchronous callback within the same thread. Of course we should better specify how it works and how it behaves.
@Emily-Jiang A use case would be
ConfigValue cfgPort = config.access("myprj.archive.port")
.as(Integer.class)
.cacheFor(2, TimeUnit.MINUTES)
.onChange(::logConfigChange);
and logConfigChange will then log out that a value got changed.
Of course there might be other tasks which get triggered when the configured value flips.
interface ConfigChanged { | ||
<T> void onValueChange(String key, T oldValue, T newValue); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that ConfigValue in this PR covers too many things at once. I'd like to split some methods into a separate PR. As a base, I think it is a no-brainer to leave as
, get
, getOptional
, getKey
and move other methods into PRs on their own. We can then discuss separately.
|
||
Assert.assertEquals(config.access("tck.config.test.javaconfig.configvalue.boolean.oui").as(Boolean.class).get(), Boolean.TRUE); | ||
Assert.assertEquals(config.access("tck.config.test.javaconfig.configvalue.boolean.oui_uppercase").as(Boolean.class).get(), Boolean.TRUE); | ||
Assert.assertEquals(config.access("tck.config.test.javaconfig.configvalue.boolean.oui_mixedcase").as(Boolean.class).get(), Boolean.TRUE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with Emily, the specification should be formal and pick only widely used options. Any implementation can support more options, and if it doesn't a custom converter should solve the problem. Otherwise, we would have to support all language variants - possibly Japanese, since Fujitsu have also joined MP.
addFile(testJar, "META-INF/microprofile-config.properties"); | ||
addFile(testJar, "sampleconfig.yaml"); | ||
|
||
WebArchive war = ShrinkWrap |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we would need to change WebArchive to JavaArchive later to allow that TCK can be passed also by implementations that don't support WAR deployments. But I agree to leave WebArchive for now, especially since I tried to hack around arquillian extensions and it is very hard, if not impossible, to turn a JavaArchive into a WebArchive before it is deployed to a container.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It originally was a JavaArchive, but I moved it to a WAR for the following reason:
WebArchive is actually the only type which is widely supported on every container. Regardless of owb and weld standalone, servlet containers or fully blown JavaEE servers.
Please don't push in ConfigValue. I don't wish to bring down this just because it is in DeltaSpike. What is the use case here? |
Each method needs to have a use case associated with. I prefer not to add this at all in the first release. I have not got any use cases for this one. |
Signed-off-by: Mark Struberg <[email protected]>
I would say the ConfigValue is not needed for Config 1.0. Please convince me otherwise. |
Signed-off-by: Mark Struberg <[email protected]>
As already written dozen times above. The following use cases get added in a clean way.
And all the work is finished. So just saying "I don't like it" is simply not a valid argument! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@struberg: the
cacheFor
of ConfigValue otoh provides means to locally cache values
I was again caught in the same trap that ConfigValue was used for 2 different concepts :( We shouldn't compare Provider and ConfigValue, because ConfigValue now is meant to be used in a programmatic way, via config.access()
, and not injected.
With CDI first approach, I would focus more on how to add this functionality to the injection point, e.g. by a @ConfigPropery(cacheFor=10, cacheForUnit=TimeUnit.SECOND) for caching.
However, caching should be quite doable even with Provider, if you wrap it into a class that caches the value for some time, so it's not necessary to have it for v1.0. And I'm still not convinced that it would be useful if config sources themselves wouldn't refresh after the cache interval. The business request of getting a new fresh value after that interval could not be met.
interface ConfigChanged { | ||
<T> void onValueChange(String key, T oldValue, T newValue); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @struberg! Now we can continue in this lengthy discussion :)
Yes @OndrejM we also should have a @Inject
@ConfigProperty(name="myprj.archive.url", cacheFor=5000)
private Provider<String> archiveUrl; |
Maybe having a separate @CacheProperty would be better:
I can create a dual PR to have all functionality proposed both in SE and CDI API. |
Please don't merge before we reach final agreement. |
@struberg I don't think the features you listed are absolutely necessary for the config 1.0. We have a lot of cleanup to do and doc/tests to update and add. The list goes on. We cannot keep on going adding more and more stuff. When I first write the proposal, I listed the dynamic support. That is one of the features to be provided and demonstrated. |
Like you did yesterday despite 2 -1 votes and open clarification requests? ;) No worries... |
The first config proposal from June 2016 did actually contain all those features. So this is not a valid argument. And it is all finished. It really works already. Tell me which use case of the 5 you think is not useful and we can discuss it. |
@struberg I'm not going to argue with you over the merging. You merged multiple commits without waiting for the final solution without any pre-warning. I messed up my environment and the commit contains multiple things which I could not lift it out. I prewarned with a comment saying it will be discussed in the hangout and it is only line and I said I would remove today if no agreement were reached. |
For the features you listed:
I am not saying they are not valid or useful. Are they must for the Config 1.0 release? I am cautious about the deadline. We have a lot of open issues to be closed in a short timeframe. |
Oki no worries, we will get it done :) |
@struberg agreed! |
+1 to leave the PR open and leave it to post 1.0 All those are nice features and I'd like to have them definitely in 1.1, but I'd like to have a CDI equivalent at the same time. For 1.0 it would be a lot of work to create the CDI equivalent, not even talking about updating the spec document and polishing TCK and javadoc (if necessary). I'd like to finalize what we have, cleanup the PRs and issues related to 1.0, and align the spec documents with all the rest. Still a lot of work to do. If all goes well, the MP umbrella could even skip config v1.0 and include config v1.1 if it is finished before MP 1.1 |
This PR was superseded by some later merged PRs. Close this one. |
This is almost the full content of ConfigValue which is up for discussion.
I completely leaves out CDI for now but I will ship this in a separate PR.
If you want to try this out then please feel free to check out https://github.com/struberg/javaConfig/tree/microprofile-impl
This contains a fully working implementation. This is basically the stuff from June 2016 but adopted to the new interface changes.
have fun!