-
Notifications
You must be signed in to change notification settings - Fork 63
Interdependencies between "things" #19
Comments
I'm not entirely sold that dependencies should be recorded in the manner they currently are. For example: function resolve_dependency(thing, property, dependee, start) {
console.log('setting ' + thing._name + "'s " + property + " to " + dependee._name);
thing[property] = dependee;
thing._unresolved--;
if (start && thing._unresolved <= 0 && thing._implementation && !thing._running) {
console.log('starting2 ' + thing._name);
thing._running = true;
thing._implementation.start(thing);
flush_queue(thing);
}
} This is recording the dependency as an actual object reference and this notion of dependencies is all over the init code. This concerns me for a few reasons. First - we're holding a long-living reference to a dependent object which is going to end up being a nightmare when it comes to keeping state consistent. At some point someone is going to update an object reference in the registry but forget to update all the objects that depend on that object. Or a dependent object is going to be removed and events need to fire updating all of these objects. It also means we have all of this logic around lazy-init to ensure that dependencies are resolved before start is called. I think a formal thing-registry could help address all of these issues. It could have a syntax like this: var registry = new Registry();
var thing1_uri = "http://.../thing1";
var thing1 = new thing(name, thing1_uri, model, etc....);
// the registry will handle dependency resolution
registry.register(thing1);
// lookups are done by uri
registry.exists(thing1_uri); // returns true
// so is searching - it uses the async pattern because it might end
// up doing any number of things to resolve - perhaps even talking to
// remote servers someday
// provide a mechanism to not resolve - maybe they just want local state
// and don't care about dependents (though the dependent URI's will still
// be in the dependents list
registry.find(thing1_uri, resolve_dependents /* bool */
function found(thing) { /* it was found */ },
function missing(err) { /* it was not found or not resolvable yet */ ); But what about dependencies? var parent = new thing(...);
parent.depends_on ("child"); // not real syntax - just communicating an unresolved dependency
registry.register(parent);
registry.find(parent_uri, true /* require resolution */
function found(thing) { /* not called because child is not resolved */ },
function missing(msg) { /* called because it is not resolved */ });
// then later ...
var child = new thing("child", ... );
registry.register(child);
registry.find(parent_uri, true /* require resolution */
function found(thing) { /* called because child is now resolved */ },
function missing(msg) { /* not called */ }); All resolution of dependencies is done within the registry - this includes both local and remote things. When something is updated, removed, added, etc - all appropriate updates are performed within the registry. There are some other side-effects of this. Consider what we have now: var thing = new thing();
registry[uri] = thing;
// then later in httpd.js
var reference_to_thing = registry[uri];
var another_ref_to_thing = registry[uri];
reference_to_thing.property = "updated";
// OOPS! Both saw the update ... that probably isn't intentional.
// There may be desirable use-cases but I bet the surprise of "spooky action from a distance"
// will be more confusing than appreciated. This is also different behavior
// depending on whether something is a proxy or not. A registry class could manage handing out new instances rather than handing out references. This will cut down on bugs on make it much safer to work with things. Thoughts? |
Pull request #32 does something like this - it's not entirely thought out, though. It's still holding on to actual object references but the result of Registry.find could just as easily be a clone (or even a "local proxy" that pub/sub'ed from the registered master if there is a desire to have read-only versus mutable). |
Web of Things servers create objects for things based upon the models in their descriptions. The implementation's start method for thing is called when all of its dependencies on other things have been resolved. The resolution of dependencies happens asynchronously, see the init_dependencies() method in framework.js. This handles dependencies on locally hosted things and on proxies for remotely hosted things. A registry is used to share the same object when several things depending on the same proxy. This approach allows for circular dependencies where one thing depends directly or indirectly on another thing that in turn depends on the first thing. The code in a thing's start method may be executed before the start methods of the thing's it depends upon, and this is inevitable in the case of circular dependencies. For proxied things, this is handled by the target server queuing messages for a thing it hosts until that thing starts. For locally hosted things, this is handled by the virtual object for the thing's implementation.
Note that an error will occur if the server is unable to set up a proxy for a remote thing. This may occur when the target server hostname isn't resolvable to an IP address, if that IP address is unreachable, if the this server isn't authorised to access the target server, or if this server is unable to open a Web Socket connection with the target server (or likewise for whichever protocol is to be used for messaging). An open question is whether to set a platform dependent timeout for resolving dependencies, and how to expose such timeouts to scripts.
Further study is needed to flesh out the details for the lifecycle of things, e.g. when a thing is unregistered then its proxies and the things that depend on it need to be notified by raising the corresponding event.
The text was updated successfully, but these errors were encountered: