-
Notifications
You must be signed in to change notification settings - Fork 787
Configuration: Move static methods to ConfigUtil. Add documentation. #5292
Conversation
17ebc58
to
e93ffe0
Compare
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 like the overall refactoring but I'm not sure about the new @RequiredField
and @UseKey
annotations: They seem not to solve a real problem we face right now. Services which are not fully configured should already log their problems and will probably not work. Using fields with different names then their counterpart in the config description is also unlikely. wdyt?
public void modified(Map<String, Object> properties) { | ||
// Print changed values | ||
MagicServiceConfig config = new Configuration(properties).as(MagicServiceConfig.class); | ||
List<Field> l = ConfigUtil.getAllFields(MagicServiceConfig.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.
Maybe instead of exposing the reflection based method getAllFields
you could give a toString
to MagicServiceConfig.
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.
True. Regarding the annotations: I actually introducted UseKey
because the configuration key of one field in MagicServiceConfig where it's used, is unfortunately called "boolean", like the Java keyword.
I'm honestly a big fan of this property-map to class object mapping, partly because I do not find that powerful reflection API in many other languages. However I'd like to see this as a default way of handling service configuration and it should work in every case.
So we either need this UseKey
annotation or we need to forbid certain configuration key names (Java language keywords).
For the time being I suggest to have the refactoring (and magic extension) only. |
Agreed. I'll open another issue for discussion. I'll rename the magic bindings configuration name from boolean to "bool". |
e93ffe0
to
15ad511
Compare
@htreu Done |
And I have added an issue for the annotations #5300. |
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 reduce visibility to private on ConfigUtil#getAllFields.
Others are only cosmetics.
* @param clazz The class | ||
* @return A list of Field objects | ||
*/ | ||
public static List<Field> getAllFields(Class<?> clazz) { |
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 make this private
again.
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.
And it would be nice to have the public API on top and the supporting private methods at the bottom of the 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.
Yup, I usually would do that, but in this case all private/public fields and methods of the ConfigUtil class are widely mixed. If I sort them now, it would be a hard job reviewing. Should be done in another PR, if that's ok.
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.
agreed, thats a pity.
But then please order at least the refactored code, so that public comes first, private comes last. And please make #getAllFields private
.
|
||
@Modified | ||
public void modified(Map<String, Object> properties) { | ||
MagicServiceConfig config = new Configuration(properties).as(MagicServiceConfig.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.
Since you introduced the new ConfigUtil#as method you can directly use it here.
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.
That would be the only place of all services where it would be different. I'd like to wait for the discussion and outcome of #5300, if that is ok for you.
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.
agreed.
15ad511
to
3d00326
Compare
@htreu done |
3d00326
to
f9f7ec5
Compare
I have sorted depending on public/private methods now. But did two more things as well:
|
Why? Everything just looked so good ;-) |
Haha yes I know. Trouble maker /me :D The consumer of the API should clearly realize that he is using an API that only works best-effort and is used in a way that it originally was not intended to be used. The one argument only method hides this fact and the best way to realize the API intention is by the mandatory second argument |
Yes, I can follow your intention. Please revert the deprecation here so we can merge the refactoring. Lets discuss this new aspect on a separate PR where others can join the discussion. I don't want to blow this one up unnecessarily. Thanks :-) |
* @return The configuration holder object. All fields that matched a configuration option are set. If a required | ||
* field is not set, null is returned. | ||
*/ | ||
public static <T> @Nullable T as(Map<String, Object> properties, Class<T> configurationClass) { |
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.
Do you have any particular reasons why this method must/should be exposed as API? I'd rather have expected to see in in a class within an internal package. In that way the Configuration
class can use it and everybody else can do a new Configuration(Map).as(Class)
if needed. What did I miss?
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.
@SJKA I tried to explain this in the PR message :)
new Configuration(Map).as(Class)
does more than we need for service configuration objects. It "normalizes" values, which basically means all numbers -> BigDecimal, Collections -> Lists.
But the ConfigUtils.as method is way smarter, because it converts the types based on the field type. So yes, it makes sense to have it public API and use ConfigUtils.as within services.
If #5300 gets realized, the as() method might move to another package though. Maybe it should be marked as deprecated so that nobody starts to use it before this is discussed.
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 tried to explain this in the PR message
If I didn't get that from your explanation, how should a user of the API figure that out? That is pretty surprising.
Another thing worth mentioning is that static utility methods only got us into trouble, both on testing/mocking side as well as with respect to evolving the APIs. We have been working in the past on getting rid of several such static helper methods and that always was painful on some end. Therefore I'd like to object to adding yet another one.
Maybe it should be marked as deprecated so that nobody starts to use it before this is discussed.
I don't know which "as" method you mean:
- Configuration.as(...) must stay where it is, that cannot be moved anymore.
- ConfigUtil.as.(...) must not be introduced publicly if we already suspect it will have to move. Why does it have to be public then if nobody should start using it?
It's hard to mock static methods true. And yes documentation of this class is not very rich. The pr addresses some methos. Please tell me how to proceed here. The mqtt stuff is once again ready and API changes of this pr are a requirement for my other PRs. The "as" method does not need to be moved in this PR, but it leads to inefficient code if it is not addressed. (Double type conversion of config values) |
@davidgraeff, @SJKA to get this one finally approved I suggest to provide the APIs just as they are available now. Please refactor the |
* getAllFields, as methods moved from Configuration to internal ConfigMapper * Add String->BigDecimal conversion in ConfigMapper.as * Add configuration holder object to MagicService of the magic binding to see if the mapping works for all kind of configuration values. * Sort ConfigUtils: public first. Signed-off-by: David Graeff <david.graeff@web.de>
f9f7ec5
to
7859b60
Compare
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.
Looks good to me, thanks!
@davidgraeff Travis fails due to missing author-tag & JavaDoc on the new ConfigMapper. Please add this asap. Thanks :-) |
Never mind, fixed it with #5320. |
(Except for the case of bug core.config.ConfigurationService IllegalArgumentException with Magic binding #5291)
Signed-off-by: David Graeff david.graeff@web.de