Conversation
Hi @sbussweiler,
now.
and all the
That is a little bit confusing. I need to inspect if the updates are generated by my handlers or the framework itself. If them are generated by my handlers: |
@maggu2810 Upon some analysis, I would assume that these log messages are due to #2096 (and not due to this PR). In #2096, I introduced a small delay before the ChannelItemProvider creates its items. The ThingManager assumes that if there is a link, the referenced item MUST exist as well and thus it sends events for it. |
@kaikreuzer I created #2121 to continue that topic as it seems to be not related (as you already stated) to this PR. |
@sbussweiler The normal startup works fine.
If my bridge goes online again, I would like to reinitialize my thing handlers (so call dispose(), initialize() -- is this okay?). What is the correct check? |
@sbussweiler Realized your comment in the other thread a little bit to late
|
@maggu2810 thanks for your feedback!
This is the default behavior implemented in
Correct!
In general |
@sbussweiler thanks for your feedback, too. @kaikreuzer Do we need any further checks before we can merge this? |
Most of the testing scenarios are:
|
As it is a huge change in the inner core, I'd also like to spend some time to review and test it before it is merged - I didn't yet find the time to do this, though. |
@sbussweiler Can you merge your branch of this PR and this one https://github.com/maggu2810/smarthome/tree/binding-test? The
Shouldn't this PR solve such a problem that a thing with a mandatory bridge is initialized without a bridge? |
How did you added the child thing? Via REST call? E.g. in PaperUI adding a child thing first, won't work AFAIK!? You have to specify the bridgeUID for the thing, otherwise the thing is not recognized as a child of a bridge. The ThingManager checks via Thing.getBridgeUID() if a thing is a child of a bridge or not.
You mean I should merge your branch into this PR branch? I didn't had time to have a look on it, sorry. Your branch is providing some test infrastructure for thing/bridge life cycle? |
No, not to this PR as it is not related.
I was able to add this thing using the Paper UI and leave the bridge selection box empty. |
I think this is what would need to happen. If there is no bridge referenced in the Thing, there is no chance to tell whether a bridge is required or not - so the framework correctly assumes the Thing can do without a bridge and thus instantiates the handler for it. |
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.
Only minor comments, LGTM
* through a {@link Bridge}. | ||
* A {@link Thing} is a representation of a connected part (e.g. physical device or cloud service) from the real world. | ||
* It contains a list of {@link Channel}s, which can be bound to {@link Item}s. | ||
* </p> |
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.
IIRC: <p>
should be used only (there is no closing paragraph tag in JavaDoc
"If you have more than one paragraph in the doc comment, separate the paragraphs with a <p> paragraph tag, as shown."
Quote: http://www.oracle.com/technetwork/articles/java/index-137868.html
The closing paragraph tag occurs multiple times in this PR, I don't comment the other occurrences.
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
* Creates a {@link ThingHandler} for the given thing. | ||
* | ||
* @param thing | ||
* thing |
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 assume we don't need a line break between parameter name and its description.
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..
* @param thing | ||
* thing | ||
* @param thing the thing that should be handled, not 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.
Can we drop that empty line?
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 empty lines are not required, but from my point of view it makes the javadoc/code more readable.
* @param callback | ||
* {@link ThingHandlerCallback} which must be used for the {@link ThingHandler} | ||
* @param thing the thing for which a new handler must be registered | ||
* |
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.
Can we drop that empty line?
* @param thing the thing for which a new handler must be registered | ||
* | ||
* @return the created thing handler instance, not 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.
Can we drop that empty line?
* @param configuration configuration | ||
* @param thingUID thing uid, which can be null | ||
* @param bridgeUID bridge uid, which 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.
Can we drop that empty line?
logger.error( | ||
"Exception occured while disposing handler of thing '" + thing.getUID() + "': " + ex.getMessage(), | ||
ex); | ||
logger.error("Exception occured while disposing handler of thing '" + thingHandler.getThing().getUID() |
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 has been wrong before your commit, but as you touched this line could you use {}
instead of string concatenation?
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, will change commented error logs.
logger.error("Exception occured during status update of thing '" + bridgeChildThing.getUID() | ||
+ "': " + ex.getMessage(), ex); | ||
logger.error("Exception occured during notification about bridge status change on thing '" | ||
+ child.getUID() + "': " + ex.getMessage(), ex); |
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 has been wrong before your commit, but as you touched this line could you use {}
instead of string concatenation?
} catch (Exception ex) { | ||
logger.error("Exception occured during bridge handler notification ('" + bridge.getUID() | ||
+ "') about handler initialization of child '{}' '" + thing.getUID() + "': " | ||
+ ex.getMessage(), ex); |
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 has been wrong before your commit, but as you touched this line could you use {}
instead of string concatenation?
} catch (Exception ex) { | ||
logger.error("Exception occured during bridge handler notification ('" + bridge.getUID() | ||
+ "') about handler disposal of child '{}' '" + thing.getUID() + "': " | ||
+ ex.getMessage(), ex); |
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 has been wrong before your commit, but as you touched this line could you use {}
instead of string concatenation?
@maggu2810 thanks for your comments, I'm back from vacation and will have a look on it! |
* | ||
* @param componentContext | ||
* component context (must not be null) | ||
*/ | ||
protected void deactivate(ComponentContext componentContext) { | ||
for (ServiceRegistration<ThingHandler> serviceRegistration : this.thingHandlers.values()) { |
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.
Hm, isn't dropping the thingHander service registrations a problem here?
If the factory goes down (because some required services are not available anymore), this meant in the past that handlers were also unregistered and hence their Things go into UNINITIALIZED/HANDLER_MISSING status.
Now, it seems that the handlers would be kept, so a binding has no possibility to dispose them anymore?
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.
There is no problem. If a factory goes all handlers are unregistered by the ThingManager. See method ThingManager.removeThingHandlerFactory()
.
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 makes the implicit assumption that a ThingHandlerFactory does not have any dynamic dependencies, but that it is deactivated on any change of its dependent services. I think this must be clearly mentioned in the documentation - it is probably best if you could add a new section that explains how to deal with service dependencies in handlers (i.e. that they do not have a bundleContext anymore and thus should not themselves deal with their dependencies, but only get them reached in from the factory through their constructor).
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 makes the implicit assumption that a ThingHandlerFactory does not have any dynamic dependencies, but that it is deactivated on any change of its dependent services.
Correct - if service references are handed over to the handlers (what will be the case in general). Services only used by the factory could be dynamic.
I think this must be clearly mentioned in the documentation
Yes, I need to make it obvious.
ThingHandler thingHandler = createHandler(thing); | ||
if (thingHandler == null) { | ||
throw new IllegalStateException(this.getClass().getSimpleName() | ||
+ " could not create a handler for the thing '" + thing.getUID() + "'."); | ||
} | ||
setHandlerContext(thingHandler); |
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 we do not treat the handler as an OSGi service anymore, I wonder if we really still need to provide the bundleContext to it? Actually, all code places that I found that are using it look at bit fishy to me, so this rather seems to provoke bad smells...
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.
Correct! I will remove the BundleContext.
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 bundle context from the BaseThingHandler
causes the following refactoring:
- The
BaseThingHandlerFactory
must handle the tracking of theThingRegistry
and theItemChannelLinkRegistry
. - Due to the fact that the
BaseThingHandlerFactory
is an abstract class we can not use declarative service features. This would end in a crappy implementation (service tracker must be used, the factory must be unregistered if theThingRegistry
disappears, etc.)
This leads me to the following question: should a binding developer use core services like ThingRegistry, ItemChannelLinkRegistry and ItemRegistry?
My proposal would be:
- A binding should not use ESH core services directly.
- Required functionality of such core services could be provided by the framework.
- E.g. the
ThingHandlerCallback
provides methods (currently used in theBaseThingHandler
) likeThingHandlerCallback.getBridge(ThingUID bridgeUID)
ThingHandlerCallback.isItemLinked(String channelId)
- ... further use cases TBD
BundleContext
,ThingRegistry
,ItemChannelLinkRegistry
would be removed from theBaseThingHandler
.
Problem:
- This will break a lot of bindings, because
BundleContext
,ThingRegistry
,ItemChannelLinkRegistry
are protected members in theBaseThingHandler
. ThingHandlerCallback.getBridge(ThingUID bridgeUID)
andThingHandlerCallback.isItemLinked(String channelId)
are the obvious use cases covered by theBaseThingHandler
. Further uses cases must be analyzed (which bindings are using the protected members for what?).
Due to the fact the this refactoring will break a lot of bindings, I would like to provide this refactoring in a separate PR. The changes of this PR are quite enough.
@kaikreuzer what do you think?
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.
BundleContext, ThingRegistry, ItemChannelLinkRegistry would be removed from the BaseThingHandler.
This imho makes sense as these services should not be directly available to binding developers.
This will break a lot of bindings
I hope that it doesn't affect to many bindings after all - from a quick check in the ESH+OH2 repos I only found 4 that would need to be changed.
I would like to provide this refactoring in a separate PR.
If this is something that is pretty isolated from the other changes, doing it in a separate PR is fine for me - I'd just like to have your confirmation that you'll nonetheless continue to work on it :-)
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.
As discussed with @kaikreuzer: BundleContext
, ThingRegistry
and ItemChannelLinkRegistry
are removed from the BaseThingHandler
in a separate PR.
* Handles a command for a given channel. | ||
* Disposes the thing handler, e.g. deallocate resources. | ||
* </p> | ||
* The framework expects this method to be non-blocking and return quickly. For longer running disposals, |
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.
Really? For a dispose this is rather unexpected to me - I would have assumed that when the method returns, the instance can really be thrown away and that it does not do any further activities in the background.
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.
In most of the cases dispose
is a short running operation. The intention was to provide the possibility to handle also long running disposals.
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 allow long running disposals. After all, this should NOT do any communication with the device, it should only clean up internal memory etc. It should be a valid assumption that the instance can be GC'ed immediately after dispose returns.
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
* of this handler does not have a bridge, this method is never called. | ||
* | ||
* @param thingStatusInfo the status info of the bridge | ||
* This method is called before a thing will be removed. An implementing class can handle the removal in order to |
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.
"before a thing is removed" instead of "before a thing will be removed"
Any guide how to get this feature to be tested on my OpenHab2 IDE environment? |
Small friendly ping as to where are standing with this as it is a requirement to solve #1847? |
afaik, we are still waiting for an update from @sbussweiler, is this right, Stefan? |
Correct, there is an ongoing discussion about a refactoring. I will add this as soon as possible. |
@kaikreuzer thanks for re-testing.
Correct. |
Didn't realize that we should not merge other stuff - but perhaps it would be the best if this one is nearly ready to merge. |
No, it is fully ok to merge other stuff - we just have to resolve conflicts of PRs afterwards that are waiting for CQ approval, that's life. |
Signed-off-by: Stefan Bussweiler <s.bussweiler@external.telekom.de>
Signed-off-by: Stefan Bussweiler <s.bussweiler@external.telekom.de>
Done. Resolved merge conflicts and changed bridgeStatusChanged() default behavior. |
FTR: Once the CQ is approved, we should merge this with keeping the 3 commits in place. |
Alright, we have check-in approval - now it counts @sbussweiler ;-) |
See eclipse-archived/smarthome#2087 Signed-off-by: Kai Kreuzer <kai@openhab.org>
See eclipse-archived/smarthome#2087 Signed-off-by: Kai Kreuzer <kai@openhab.org>
BridgeHandler
of a bridge is initialized beforeThingHandler
s of its child things are initialized.BridgeHandler
is disposed after allThingHandler
s of its child things are disposed.BridgeHandler
) is notified if a child handler is initialized/disposed.ThingHandler.bridgeHandlerInitialized()
andThingHandler.bridgeHandlerDisposed()
are removed. This breaks the API.Signed-off-by: Stefan Bussweiler s.bussweiler@external.telekom.de