-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
[Loxone] Added null annotations #2604
Conversation
@kaikreuzer @martinvw - any chance for a review? Thanks |
1 similar comment
@kaikreuzer @martinvw - any chance for a review? Thanks |
@ppieczul We do not have any final agreement on how to use the null annotations - we would like to have some guidelines that are followed in the same way throughout the codebase, so that it is clear to contributors, what is expected from them. We especially didn't (yet) decide to use any of the "*ByDefault". Please find the current discussion here and feel free to involve yourself in it! (Your PR might be a good reference for pros//cons for the different options). |
@ppieczul I believe there a conclusion to the discussion, can you confirm whether this PR already follows those new guidelines. Thanks! |
I will check it over the weekend. Thanks.
…On Fri, 22 Sep 2017 at 14:38, Martin van Wingerden ***@***.***> wrote:
@ppieczul <https://github.com/ppieczul> I believe there a conclusion
<https://github.com/eclipse/smarthome/pull/4275/files#diff-24ba7e763168d1631a43ebdef19bcd5d>
to the discussion, can you confirm whether this PR already follows those
new guidelines.
Thanks!
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<https://github.com/openhab/openhab2-addons/pull/2604#issuecomment-331434968>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AJx5fqNIj7UGTRSWvo4BFB6oGkFGarksks5sk6ougaJpZM4PD2Oc>
.
|
@martinvw I think I need your help here. I made a new installation of eclipse IDE and upon running a debug configuration I am getting unresolved upnp bundle probably due to unresolved apache http.
This prevents from running bindings that use upnp, for example hue which is enabled in the default configuration. I tried purging eclipse and its hidden folders and reinstalling, but same story. Do you have any ideas how to overcome this? Thanks |
First, the problem with unresolved references under IDE seems to be random. It depends on how the eclipse starts. Sometimes after performing all the initial activities, the problem exists (as long as eclipse is open). But sometimes, after subsequent eclipse restart, the problem goes away and everything works fine as long as eclipse session is open. I don't plan to investigate it. @martinvw I think my changes conform to the general idea of having classes non null by default, with Nullable added wherever it is needed. I added package-info files to each package, as this forces future new classes to follow this guideline. I updated the PR to the latest code and it should be ready for review/merge (if the checks don't fail...) |
FYI: In ESH we considered not using |
If this is the direction, let it be. I changed the code to follow it. |
@martinvw @kaikreuzer would it be a good time now to review and merge these changes? Thanks. |
@martinvw: I merged with the top code. Kindly asking for a review and merge. Thanks. |
@ppieczul please realise that we have quite some queue and your earlier contributions already jumped the queue, so maybe we can make an arrangement. If you could review some other PR(s) then I will make sure this gets reviewed and merged ASAP. Hereby some (more or less random) suggestions:
I always use the following filters to find PR's which are ready to be reviewed: https://github.com/openhab/openhab2-addons/pulls?page=2&q=is%3Apr+is%3Aopen+-label%3A%22Work+in+progress%22+-label%3A%22Awaiting+feedback%22+-assignee%3Akaikreuzer&utf8=%E2%9C%93 |
Haha, @ppieczul that's a broad hint that you seem to be a good candidate to help us a bit on reviews. What do you think? It would help making us all faster and reduce the waiting time for everyone - would be cool to have you helping out! 😄 |
@@ -201,16 +211,32 @@ public void channelLinked(ChannelUID channelUID) { | |||
@Override | |||
public void initialize() { | |||
logger.trace("Initializing thing"); | |||
|
|||
// config.as does not put restrictions on the return time (it is nullable) |
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.
Is this an error in the framework should we change it there?
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.
PS: typo, time instead of type
} | ||
channels.addAll(newChannels); | ||
for (Channel channel : newChannels) { | ||
@SuppressWarnings("null") |
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.
Is this correct, or is this a bug in framework? Or does it somehow think that the channel can be null?
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.
Channel class seems to be null by default (it has some of the fields annotated with NonNull). But uid can be null and there actually is a (package protected only) default constructor where uid is not initialized. I thing this can be fixed in the Channel class, probably by simply initializing uid to a new ChannelUUID() in the default constructor and annotating this as non null. I will remove this suppression and leave the warning, then try fix it in the framework.
this.name = json.name; | ||
String jsonName = json.name; | ||
if (jsonName != null) { | ||
this.name = jsonName; |
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.
Why not allow the field to be null to represent an unknown name, imho null's are not that evil, they have there place in the ecosystem (for now)
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.
Originally I checked for this null anyway, but later, and substituted it with 'unknown'.
@@ -225,17 +239,20 @@ void update(LxJsonControl json, LxContainer room, LxCategory category) { | |||
String value = element.getAsString(); | |||
if (value != null) { | |||
LxUuid id = new LxUuid(value); | |||
@SuppressWarnings("null") |
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 personally think, because of the many repeated suppressions, that this not really mature yet I see some drawback in the readability of the code.
@SuppressWarnings("null")
@NonNull
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 should be better when suppressions are removed from the jdk calls. The ones left will be for GSON and logger. Do you know if there is plan to fix it for the logger? This is common enough to be important.
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 @triller-telekom knows?
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 are planning to revise our guidelines where we now state that @NonNullByDefault
should be used per class. depending on the outcome of #4321 we might even disable the warnings in the IDE and only show the error cases.
Regarding third party libraries there is also PR #4217 but that one is waiting for the outcome of #4321, too.
But if you want to keep using the null annotations, please change them to the @NonNullByDefault
on class level.
Seems like the whole null annotations thing went into business for itself even before we had agreed on the above mentioned guidelines. I am sorry if this now hits you so you have to change your annotations.
logger.trace("Existing state for LxControl {}: {}", json.type, name); | ||
@SuppressWarnings("null") | ||
@NonNull | ||
LxControlState state = states.get(name); | ||
state.getUuid().setUpdate(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.
Don't you have to call states.put(name, state);
anymore? Or was that an error because you just restored the existing state?
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.
Yes, this was an error, or a redundant put changing nothing, the key is already there and we don't change the object reference.
String name = room.name; | ||
if (uuid != null && name != null) { | ||
addOrUpdateRoom(new LxUuid(uuid), name); | ||
} |
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.
Should we do any logging this feels like a non-happy-flow
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.
addOrUpdateRoom is a normal flow, I could add a log to the 'else' part, which is absent now.
org.osgi.framework, | ||
org.slf4j | ||
Service-Component: OSGI-INF/*.xml | ||
Manifest-Version: 1.0 |
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 a proper resolving of this merge conflict, please follow the new sorting / indentation etc and only add your new imports
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.
Did you address this one?
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.
Yes you did, only github did not show that properly :-)
/** | ||
* An event received from Loxone Miniserver with control's state update | ||
* | ||
* @author Pawel Pieczul - initial contribution | ||
* | ||
*/ | ||
@NonNullByDefault |
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.
Would it be less configuration, to not use this one 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.
This will result in many incompatibilities in classes that use this class and are non null by default (or we mark all method arguments in this class as non null, which is even more ugly)
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.
Okay I did not completely realise the impact 🙂
String cleanupUuid(String uuid) { | ||
@SuppressWarnings("null") | ||
@NonNull | ||
String newId = uuid.replaceAll("[^a-zA-Z0-9-]", "-").toUpperCase(); |
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.
Wouldn't it make more sense to annotate the method and replace the body with:
return uuid.replaceAll("[^a-zA-Z0-9-]", "-").toUpperCase();
String type = cat.type; | ||
if (uuid != null && name != null && type != null) { | ||
addOrUpdateCategory(new LxUuid(uuid), name, type); | ||
} |
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.
See above
@kaikreuzer @martinvw I will be honored to help, as far as my competence and time allows. Let me start with #2350 this weekend. |
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.
@ppieczul some final remarks :-)
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Unknown host"); | ||
try { | ||
server = createNewServer(cfg); | ||
if (server == null) { |
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.
Minor since you are already handling exceptions, maybe you might be better of not returning null
from createNewServer()
and throw an exception in that error scenario, then both the OFFLINE markings end up on the same level,
public class LoxoneHandlerFactory extends BaseThingHandlerFactory { | ||
|
||
@SuppressWarnings("null") |
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 you could consider leaving this ones out
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.
Removing the @SuppressWarnings
here will give a compiler warning....
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.
Sets
comes from com.google.common.collect package and we talked about not suppressing only JDK calls, that's why I left it. However, I am ok with removing this and leaving the warning.
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 is a valid 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.
what about dropping the unnecessary dependency to the google library and just use
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<>(LoxoneBindingConstants.THING_TYPE_MINISERVER);
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.
Probably better to use: Collections.singleton()
. Which is also standard java.
public class LoxoneHandlerFactory extends BaseThingHandlerFactory { | ||
|
||
@SuppressWarnings("null") | ||
@NonNull |
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 remove this
logger.trace("Reading states for LxControl: {}", json.type); | ||
|
||
for (Map.Entry<String, JsonElement> jsonState : json.states.entrySet()) { | ||
@SuppressWarnings("null") | ||
@NonNull |
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 remove all @nonnull
Map<String, LxJsonControl> controls; | ||
Map<String, LxJsonRoom> rooms; | ||
Map<String, LxJsonCat> cats; | ||
Map<@NonNull String, @NonNull LxJsonControl> controls; |
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 remove all @nonnull and use @NonNullByDefault
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.
Then I have to add nullable to every field, since they can be null. It also gives me over 20 new warnings due to potential null access, which are these warnings for potential change of the value in multithreaded code, which will never happen here. For example:
if (a != null) {
if (a.b != null) {
a.b.doSomething();
}
}
This will generate warning at doSomething, because a
might have changed between a!=null
and this line.
I think it is not worth it and would rather leave the class not annotated. But then I have to mark the Map key and value and nonnull, because they are never created null, if map is created at all and this seems to break the convention to not use nonnull.
What do you think, which is better? Maybe another solution exists, which I can't see now.
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 was triggered by the remark of @triller-telekom maybe he knows more.
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.
@triller-telekom can you enlighten us?
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 you want to join in eclipse-archived/smarthome#4321 with these observations/problems. The more we use those annotations, the less we are confident that it is possible to solve all the warnings in a decent way...
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.
@kaikreuzer @ppieczul I just read the latest state of the discussion and now I'm in doubt should we just merge this as is. Or do we still want to wait for things to settle down. I think that I read that the usage of @nonnullbydefault is still the prefered way, but not all warnings have to be solved / surpressed?
I think it would be great of one of people actively involved in the discussion could review this PR, maybe @htreu @triller-telekom or @SJKA? Thanks all!
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 would be great to have this reviewed by more people.
If there are generally no erroneous changes here, I think (in the spirit of continuous integration) it is more beneficial to merge this and then resolve outstanding warnings at the JDK calls - when they are solved at the framework level.
I will also summarize my findings in the smarthome disscuscion mentioned above.
With more and more nonnullbydefault added to the framework, I am now getting lots of warnings from this binding too with the version that is on master.
Also, there is a new version of Loxone server software which requires new things to be added to the binding, so it starts stacking up.
Thanks
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 vote for adding @NonNullbyDefault
to the class and if your variables can all be null
you would need to add the @Nullable
to them.
And please add your findings regarding false positives warnings about the null annotations to our discussion in eclipse-archived/smarthome#4321. The outcome of this discussion will probably be that we change the settings within Eclipse about what we will treat as a warning/error.
For sure we should not block PRs for senseless discussions, but in this case we have the problem that we introduced the null annotations to the framework before we actually agreed on rules on how to use them :( This was a bit unfortunate because while using those annotations they turned out to be rather tricky to use and the tools (like Eclipse) do not support them perfectly. I am sorry that you had to change your PR quite some often because of this, but you were a little to quick for us ;)
if (resp != null) { | ||
try { | ||
@SuppressWarnings("null") | ||
@NonNull |
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 remove all @nonnull and use @NonNullByDefault
if (resp != null) { | ||
try { | ||
@SuppressWarnings("null") | ||
@NonNull |
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 remove all @nonnull and use @NonNullByDefault
if (config != null) { | ||
try { | ||
@SuppressWarnings("null") | ||
@NonNull |
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 remove all @nonnull and use @NonNullByDefault
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 see my comments inline in the code.
Regarding the left over warnings, please see eclipse-archived/smarthome#4321 where we are about to change the warning/error settings.
} | ||
|
||
@Override | ||
public void dispose() { | ||
logger.debug("Disposing of server"); | ||
if (server != null) { | ||
server.stop(); | ||
LxServer srv = server; |
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.
Why are you copying the class variable into a local one before checking for null
and stopping the server?
public class LoxoneHandlerFactory extends BaseThingHandlerFactory { | ||
|
||
@SuppressWarnings("null") |
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 about dropping the unnecessary dependency to the google library and just use
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<>(LoxoneBindingConstants.THING_TYPE_MINISERVER);
@@ -102,11 +139,13 @@ public LxControlState getState(String name) { | |||
* Call when control is no more needed - unlink it from containers | |||
*/ | |||
public void dispose() { | |||
if (room != null) { | |||
room.removeControl(this); | |||
LxContainer localRoom = room; |
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.
Same as above: Why do you copy the class variable into a local one?
} | ||
if (category != null) { | ||
category.removeControl(this); | ||
LxCategory localCat = category; |
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.
Same as above: Why do you copy the class variable into a local one?
Map<String, LxJsonControl> controls; | ||
Map<String, LxJsonRoom> rooms; | ||
Map<String, LxJsonCat> cats; | ||
Map<@NonNull String, @NonNull LxJsonControl> controls; |
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 vote for adding @NonNullbyDefault
to the class and if your variables can all be null
you would need to add the @Nullable
to them.
And please add your findings regarding false positives warnings about the null annotations to our discussion in eclipse-archived/smarthome#4321. The outcome of this discussion will probably be that we change the settings within Eclipse about what we will treat as a warning/error.
For sure we should not block PRs for senseless discussions, but in this case we have the problem that we introduced the null annotations to the framework before we actually agreed on rules on how to use them :( This was a bit unfortunate because while using those annotations they turned out to be rather tricky to use and the tools (like Eclipse) do not support them perfectly. I am sorry that you had to change your PR quite some often because of this, but you were a little to quick for us ;)
@@ -255,22 +260,22 @@ boolean connect() { | |||
|
|||
synchronized (state) { | |||
socket = new LxWebSocket(); | |||
wsClient = new WebSocketClient(); | |||
WebSocketClient client = new WebSocketClient(); | |||
wsClient = client; |
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.
Why do you create a local variable at all?
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 to avoid a warning about potential null access, because theoretically the indirect reference may have changed between the check and use.
‘’’
if (a.b != null) {
x(a.b)
y(a.b)
}
‘’’
Apparently in constructions like that compiler will not optimize to cache the referenced value, because this is not what we ask for coding it like that. If the code flow allows for preemption to another thread, the subsequent use of a.b may find b as null. This is my explanation as to why there is a warning.
This leads to awkward constructions with local variables. I would like to eliminate that and this may lead to warning level tuning but I don’t remember now which one was responsible for that.
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.
If you just introduced the local variable to satisfy the warnings of the eclipse nullchecker, please undo your changes. We definitely do not want to code around those issue.
But as it is within a synchronized
block I am assuming that wsClient
can indeed be changed by another thread, is that correct? If not, please undo your changes as I mentioned above.
Set NonNullByDefault on all packages except thing factory. Removed dead code and redundant null checks, added missing null checks. Added thread-safe constructions with local object references to eliminate possible null access warnings. Signed-off-by: Pawel Pieczul <pieczul@gmail.com> (github: ppieczul)
Signed-off-by: Pawel Pieczul <pieczul@gmail.com> (github: ppieczul)
Signed-off-by: Pawel Pieczul <pieczul@gmail.com> (github: ppieczul)
Signed-off-by: Pawel Pieczul <pieczul@gmail.com> (github: ppieczul)
Signed-off-by: Pawel Pieczul <pieczul@gmail.com> (github: ppieczul)
Signed-off-by: Pawel Pieczul <pieczul@gmail.com> (github: ppieczul)
…trolDimmer. Signed-off-by: Pawel Pieczul <pieczul@gmail.com> (github: ppieczul)
…simplify the code of control classes. Signed-off-by: Pawel Pieczul <pieczul@gmail.com> (github: ppieczul)
The warnings left are related to JDK calls that show nullness incompatibilities. Signed-off-by: Pawel Pieczul <pieczul@gmail.com> (github: ppieczul)
Signed-off-by: Pawel Pieczul <pieczul@gmail.com> (github: ppieczul)
…by default to JSON deserialization classes. Signed-off-by: Pawel Pieczul <pieczul@gmail.com> (github: ppieczul)
Signed-off-by: Pawel Pieczul <pieczul@gmail.com> (github: ppieczul)
I removed the extra variable as @triller-telekom suggested and also made the JSON deserialization classes non null by default. |
I am putting this PR on hold. I need merging of #2786 first. This will be followed by implementation of new controls and authentication mechanism that were introduced to the most recent version of Loxone Miniserver software. These are more important than null annotations. I will reopen this PR once the other PRs are done. |
Signed-off-by: Pawel Pieczul pieczul@gmail.com (github: ppieczul)