stasher::manager
->manage_hierarchymonitor
(): maintaining an internal snapshot of an object hierarchy#include <stasher/client.H> #include <stasher/manager.H> #include <stasher/managedhierarchymonitor.H> #include <x/destroycallbackflag.H> // An example of a stasher::managedhierarchymonitorObj implementation. class mymonitorObj : public stasher::managedhierarchymonitorObj { public: std::string hierarchy; mymonitorObj(const std::string &hierarchyArg) : hierarchy(hierarchyArg) { if (hierarchy.size() == 0) hierarchy="[root]"; } ~mymonitorObj() { } // Invoked when the connection gets established, or breaks. void connection_update(stasher::req_stat_t status) override { std::cout << ("Connection update: " + x::tostring(status) + "\n") << std::flush; } // Initial contents of the hierarchy follow, until the next enumerated // call. void begin() override { std::cout << ("=== Initial contents of " + hierarchy + "\n") << std::flush; } // End of the initial contents of the hierarchy void enumerated() { std::cout << ("=== End of " + hierarchy + "\n") << std::flush; } // An object in the hierarchy has been added or updated. // After enumerated(), this gets called whenever an object in the // hierarchy gets changed. void updated(const std::string &objname, const x::uuid &objuuid) { std::cout << (objname + " (" + x::tostring(objuuid) + ")\n") << std::flush; } // An object in the hierarchy has been removed. Typically follows // enumerated(), as needed. // // It's possible that: // // - removed() also gets called after begin() but before // enumerated(), referring to an object that's already been reported // as updated(), or for an object that has not been reported as // updated(). // // - During enumeration, updated() gets called more than once with the // same objname and uuid. // // This can happen when a transaction updates the hierarchy while it // is getting enumerated. // // Applications that maintain an internal snapshot of the hierarchy // can implement begin() by clearing the internal snapshot, then // applying updated/removed callback, as they come in; then when // enumerated() gets received, the internal snapshot now matches what's // in the repository. Applications should not consider a removed() // for an object they do not have a previous update() to be an error // condition, neither an update() with the object name and uuid // unchanged. void removed(const std::string &objname) { std::cout << (objname + " (removed)\n") << std::flush; } }; void monitor(const std::list<std::string> &hierarchies) { x::destroyCallbackFlag::base::guard guard; auto client=stasher::client::base::connect(); guard(client); // When we return from this func, make sure this goes away. auto manager=stasher::manager::create(L"", "10 seconds"); std::cout << "Starting subscriptions, press ENTER to stop" << std::endl; std::list<x::ref<mymonitorObj> > monitors; std::list<x::ref<x::obj> > mcguffins; for (auto &hierarchy:hierarchies) { auto monitor=x::ref<mymonitorObj>::create(hierarchy); monitors.push_back(monitor); mcguffins.push_back(manager-> manage_hierarchymonitor(client, hierarchy, monitor)); } std::string dummy; std::getline(std::cin, dummy); } int main(int argc, char **argv) { try { std::list<std::string> args(argv+1, argv+argc); if (args.empty()) args.push_back(""); monitor(args); } catch (const x::exception &e) { std::cerr << e << std::endl; exit(1); } return 0; }
This is the equivalent of a
()
for hierarchies.
This example differs from
stasher::manager
->manage_objectsubscribe.C
,
that uses subscribe
() by what information its
callback gets.
For a subscribed hierarchy,
subscribe
()'s
updated
() callback reports that something about
a particular object in the hierarchy has changed: it was either created,
updated, or removed, and it's up to the application to try to retrieve it,
and report the results.
This is what, essentially, a manager does for a managed hierarchy, then
updated
() for a new or an updated object's
uuid, or removed
() for a removed object.
Define a subclass of
stasher::managedhierarchymonitorObj
and implement its methods, as shown in this example.
()
takes three parameters: a client connection, the name of a hierarchy
(an empty string for the top level, root hierarchy, or a hierarchy name
without the trailing “/”),
and an stasher::manager
->manage_hierarchymonitorx::ref
to a subclass of
stasher::managedhierarchymonitorObj
.
As shown in this example, the same instance can be installed for more than
one managed object.
()
returns a mcguffin that represents the managed hierarchy. There is no
explicit un-manage method. The manager stops managing the hierarchy when
its mcguffin goes out of scope and gets destroyed.
stasher::manager
->manage_hierarchymonitor
The
connection_update
() callback reports the status
of the subscription.
connection_update
() gets invoked shortly after
manage_hierarchymonitor
() returns (if not right before it),
and any time the status of the connection with the server changes.
stasher::req_processed_stat
reports a success in
opening the managed object's subscription.
A connection_update
() of
stasher::req_disconnected_stat
indicates that the
connection with the stasher server was lost. In the following example, the
connection
gets lost, and then reestablished by the manager shortly thereafter.
Run hierarchymonitor.C
without any arguments to monitor
the top level, root hierarchy. Alternatively, names of one or more
hierarchies are given as parameters. The main execution thread sets up the
subscriptions, and waits for Enter. All the output comes
from the callbacks, as documented in the example.
Start this example, subscribing to one or more objects or hierarchies,
then use simpleput.C
or stasher to
create, update, and remove the objects in the monitored hierarchies.
When monitoring more than one hierarchy, their output can get intermixed,
naturally:
$ ./hierarchymonitor "" "fruits"
Starting subscriptions, press ENTER to stop
Connection update: Transaction/request processed
=== Initial contents of [root]
Connection update: Transaction/request processed
=== Initial contents of fruits
=== End of [root]
fruits/apple (zzS9QJ2JoDfj9000h8FmJm0000126mO00318n4AS)
=== End of fruits
fruits/banana (_DS9QJ2JoDfj9000h8FmJm0000126mO00318n4AS)
fruits/apple (0TW9QJ2JoDfj9000h8FmJm0000126mO00318n4AS)
fruits/banana (removed)
Connection update: Connection to the server failed
Connection update: Connection to the server failed
Connection update: Transaction/request processed
=== Initial contents of fruits
Connection update: Transaction/request processed
=== Initial contents of [root]
=== End of [root]
fruits/apple (0TW9QJ2JoDfj9000h8FmJm0000126mO00318n4AS)
=== End of fruits
In this example, the top level hierarchy initially has no objects, and the “fruits” hierarchy has an “apple” object. The initial list of objects in each hierarchy is intermixed.
Following the initial list of objects in hierarchy, some updates are shown, then the connection to the object repository server breaks, for some reason. Some time later the connection gets reestablished, for both subscriptions. This results in getting an another list of objects in each hierarchy. This is because the list of objects in each hierarchy could've changed, in the mean time (it didn't here, but it could).
The manager object takes care of reconnecting. All the callbacks in this example have certain limitations, see the section called “What asynchronous C++ API methods can and cannot do” for more information.
Each managed hierarchy counts towards the client's connection limits.