Merge lp:~marcustomlinson/unity-scopes-api/reg_file_system_monitor into lp:unity-scopes-api/devel
- reg_file_system_monitor
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Michi Henning |
Approved revision: | 424 |
Merged at revision: | 358 |
Proposed branch: | lp:~marcustomlinson/unity-scopes-api/reg_file_system_monitor |
Merge into: | lp:unity-scopes-api/devel |
Diff against target: |
1716 lines (+1057/-151) 29 files modified
debian/changelog (+10/-0) debian/libunity-scopes1.symbols (+1/-1) include/unity/scopes/Registry.h (+9/-0) include/unity/scopes/internal/MWRegistry.h (+8/-0) include/unity/scopes/internal/RegistryImpl.h (+1/-0) include/unity/scopes/internal/RegistryObject.h (+6/-1) include/unity/scopes/internal/zmq_middleware/ZmqSubscriber.h (+1/-1) include/unity/scopes/testing/MockRegistry.h (+1/-0) scoperegistry/CMakeLists.txt (+2/-0) scoperegistry/DirWatcher.cpp (+272/-0) scoperegistry/DirWatcher.h (+79/-0) scoperegistry/FindFiles.cpp (+35/-23) scoperegistry/FindFiles.h (+17/-9) scoperegistry/ScopesWatcher.cpp (+184/-0) scoperegistry/ScopesWatcher.h (+57/-0) scoperegistry/scoperegistry.cpp (+136/-100) src/scopes/internal/MWRegistry.cpp (+24/-1) src/scopes/internal/RegistryImpl.cpp (+5/-0) src/scopes/internal/RegistryObject.cpp (+32/-2) src/scopes/internal/zmq_middleware/ZmqSubscriber.cpp (+11/-4) test/gtest/scopes/Registry/CMakeLists.txt (+1/-0) test/gtest/scopes/Registry/Registry_test.cpp (+138/-2) test/gtest/scopes/Registry/other_scopes/CMakeLists.txt (+2/-0) test/gtest/scopes/Registry/other_scopes/testscopeC/CMakeLists.txt (+1/-0) test/gtest/scopes/Registry/other_scopes/testscopeC/testscopeC.ini.in (+8/-0) test/gtest/scopes/Registry/other_scopes/testscopeD/CMakeLists.txt (+1/-0) test/gtest/scopes/Registry/other_scopes/testscopeD/testscopeD.ini.in (+8/-0) test/gtest/scopes/internal/RegistryObject/RegistryObject_test.cpp (+1/-1) test/gtest/scopes/internal/zmq_middleware/RegistryI/RegistryI_test.cpp (+6/-6) |
To merge this branch: | bzr merge lp:~marcustomlinson/unity-scopes-api/reg_file_system_monitor |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Michi Henning (community) | Approve | ||
Review via email: mp+220501@code.launchpad.net |
Commit message
Introduced Dir/ScopesWatcher classes to watch for updates to the scope install directories, and updated all relevant registry code to propagate an update pub/sub message via middleware.
Description of the change
Marcus Tomlinson (marcustomlinson) wrote : | # |
Marcus Tomlinson (marcustomlinson) wrote : | # |
Although, the change I made doesn't actually break backward compatibility, I've simply added functionality. Do I still need to bump the version number?
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:394
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Michi Henning (michihenning) wrote : | # |
Yes, the version needs incrementing because you added a new virtual method to a public class. That changes the vtbl layout, so the change is not ABI compatible.
You will need to update the symbols file with the added symbols. Instructions for doing this are in HACKING.
The MICRO version in CMakeLists.txt needs updating.
In addition, debian/changelog needs updating. From the root directory (not the debian directory):
$ dch -v '0.4.8+
Note the 0.4.8 in the version string, which has to match whatever is in CMakeLists.txt.
Michi Henning (michihenning) wrote : | # |
Minor educational rant below :-)
static const int c_buffer_len = 1024 * (c_event_size + 16);
I'm not too keen on the magic numbers here. I had to read the inotify() man page to figure out what's going on.
A better way to size the read buffer might be to call select() to wait until the descriptor becomes ready for reading. Then use ioctl(fd, FIONREAD, &avail) to get the number of available bytes.
Then use avail to size the buffer for reading.
Minor comment:
int length = read(fd_, buffer, c_buffer_len);
This is a little brittle. Personally, I prefer
int length = read(fd_, buffer, sizeof(buffer));
It doesn't matter all that much here but, in situations where there are several buffers involved, this is a little bit more robust. (Note that this will *not* work if the buffer is dynamically allocated and declared as char* or char[].)
Still better:
string s;
s.reserve(avail);
s.size(avail);
int length = read(fd_, &s[0], s.size());
No more magic numbers that way, and it gets extremely unlikely that a future code modification will pass a mismatched buffer and buffer size to read().
Doubleplusgood:
A Buffer class that's instantiated with a file descriptor and buries all the range checking inside its methods. But that would be overkill here, so please don't do that! :-)
if (fd_ < 0)
{
throw ResourceExcepti
}
The error message isn't great, because it doesn't say why it broke. Throw a unity::
if (length < 0)
{
throw ResourceExcepti
}
Same here: knowing the errno in this case would be nice, so SyscallException is better, and adding the value of fd to the message would be a nice touch.
When this exception is thrown, it doesn't have anywhere to go because the enclosing try-catch swallows it. So, in effect, all the exception does is cause the thread to exit and all the love of the exception is actually lost :-)
Seeing that a read failure is tantamount to an assertion failure (I can't see any reasonable circumstances under which this might fail), it might be easier to just write a message with the fd and errno to stderr and throw 99 to get out of there :-)
The exception handler sets thread_state_ to Failed, but the Failed state is never used elsewhere. Moreover, the next call to remove_watch() will overwrite the Failed state with the Stopping state.
So, as is, the Failed state has no purpose. If there is a Failed state, add_watch() and remove_watch() should probably throw something when called in the Failed state. Of course, what they throw could be an exception that was deposited by the thread into an exception_ptr member, which would allow the exception caught by the watcher thread to eventually make its way back to the caller :-) The destructor could look at the state and print the exception_ptr what() string to cerr. That would deal with the situation where no call to add_watch() or remove_watch() was made after the watcher thread fell over.
Michi Henning (michihenning) wrote : | # |
949 + RegistryObject:
Why this change, and the check for a non-null middleware? Can you explain?
Michi Henning (michihenning) wrote : | # |
When I run the test in a loop, I get a failure within a few dozen iterations:
[ RUN ] Registry.
/home/michi/
Value of: list.size()
Actual: 2
Expected: 3
I'm not sure yet whether this is simply because the registry hasn't reacted to the update yet, and the call to list() is happening too early, or because there is something more serious. But it needs looking at.
Michi Henning (michihenning) wrote : | # |
Niggle:
#include "DirWatcher.h"
is redundant in scoperegistry.cpp because ScopesWatcher.h includes that.
I think it would be a nice touch to add some trace to ScopesWatcher that reports when it detects addition, removal, or update of something. This will make it easy for someone to confirm that, when something in the scope config is changed, the registry has indeed reacted to it.
What happens if a scope is *updated* rather than added or removed? I guess this is impossible under normal circumstances? Or is there a way that could happen if a package is updated? If so, we probably need to look for writes to the .ini files and react to them as well as looking for updates to the .so files. (Not sure whether this is really necessary, probably best if we discuss this.)
- 395. By Marcus Tomlinson
-
Updated symbols
- 396. By Marcus Tomlinson
-
Bumped version number
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:396
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 397. By Marcus Tomlinson
-
Got rid of magic numbers, and improved exception detail in watch_thread.
- 398. By Marcus Tomlinson
-
If watch_thread is aborted, rethrow the exception on add/remove_watch(), and print exception on destruction.
- 399. By Marcus Tomlinson
-
Remove redundant #include
Marcus Tomlinson (marcustomlinson) wrote : | # |
> 949 + RegistryObject:
> RegistryObject(
>
> Why this change, and the check for a non-null middleware? Can you explain?
In order for the RegistryObject to create a publisher on the middleware it needs the middleware pointer to do so. I figured the least intrusive change would be to make this parameter optional. If the pointer is not provided, we simply do not register the publisher.
I'm not sure, is this insane? :P
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:399
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Michal Hruby (mhr3) wrote : | # |
> Yes, the version needs incrementing because you added a new virtual method to
> a public class. That changes the vtbl layout, so the change is not ABI
> compatible.
Strictly speaking, as long as the new method is appended to the vtable (which it is), it is ABI compatible, cause users of the lib always get the instance from the lib itself.
But it adds API, so yea, MICRO version should be bumped, plus debian changelog updated.
Michi Henning (michihenning) wrote : | # |
> In order for the RegistryObject to create a publisher on the middleware it
> needs the middleware pointer to do so. I figured the least intrusive change
> would be to make this parameter optional. If the pointer is not provided, we
> simply do not register the publisher.
>
> I'm not sure, is this insane? :P
Wouldn't say that it's insane, exactly, but weird :-)
A RegistryObject can't really exist unless there is a middleware for it.
Does the publisher really have to be created by the RegistryObject? If so,
by all means, pass a middleware pointer to the constructor. But that pointer cannot ever be nullptr, as far as I can see.
Or, in scoperegistry.cpp, create the publisher separately and pass the publisher to the RegistryObject. Either one of these might do? I was mainly confused be the optional middleware pointer, which doesn't seem to make sense for a RegistryObject.
BTW, you can always get at the middleware via the RuntimeImpl (mw_base()). Or, inside an operation implementation, you can get at the middleware via the InvokeInfo object.
Michi Henning (michihenning) wrote : | # |
> Strictly speaking, as long as the new method is appended to the vtable (which
> it is), it is ABI compatible, cause users of the lib always get the instance
> from the lib itself.
We can't rely on that because the standard doesn't even know about the existence of a vtbl. For all we know, the compiler sorts the vtbl alphabetically by method name… It might happen to work for gcc, if gcc happens to preserve the order in which methods are declared in the vtbl, and the new method is appended to all the others. But that's not something we should rely on.
Jussi Pakkanen (jpakkane) wrote : | # |
Michi Henning (michihenning) wrote : | # |
> Yes and no:
>
> http://
Yes, OK, with gcc and clang, we'd actually get away with it.
I'd still prefer to change the version number though. Otherwise, I can write an application that calls the newly-added function and works fine, and then copy the binary for the application to a different machine that has the older version of the library, and the app will fall over when I try to run it because the symbol isn't in the library.
Yes, I know, that's forward compatibility, not backward compatibility. Still, changing the version number is easy and guaranteed to avoid any confusion, so we might as well do it.
Jussi Pakkanen (jpakkane) wrote : | # |
You have a trigger here for file creation. That produces a (admittadly very, very, very unlikely) race condition where you start using the file before it is fully written.
If you want to be extra careful, you should use IN_CLOSE_WRITE instead of IN_CREATE for files. I would not say this is a blocker so feel free to do what you deem best.
- 400. By Marcus Tomlinson
-
ScopesWatcher now watches for updates in .so files, and updates the registry accordingly.
- 401. By Marcus Tomlinson
-
Thread safety
- 402. By Marcus Tomlinson
-
Added logging for (un)installation of scopes
- 403. By Marcus Tomlinson
-
Updated RegistryObject to expect a middleware pointer on construction, and fixed tests accordingly.
- 404. By Marcus Tomlinson
-
Merged devel
- 405. By Marcus Tomlinson
-
Fixed changelog
- 406. By Marcus Tomlinson
-
Block test until updatesa are received from the registry
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:402
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:406
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 407. By Marcus Tomlinson
-
Added -f arguments to system calls in Registry_test
- 408. By Marcus Tomlinson
-
Check return values of system() calls, and log verbose output of each command.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:407
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:408
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Marcus Tomlinson (marcustomlinson) wrote : | # |
Michi: Ok, so apart from fixing my dodgey test, I've made all the changes you've suggested. Please could you have another look. Thanks!
Michi Henning (michihenning) wrote : | # |
> Michi: Ok, so apart from fixing my dodgey test, I've made all the changes
> you've suggested. Please could you have another look. Thanks!
Will do, thanks!
Jeeesus, what are you doing coding at this time of night? What's the local time where you are? 4:00am?
Michi Henning (michihenning) wrote : | # |
219 + throw ResourceExcepti
That one should be SyscallException, I think, so we get the errno.
vector<string> find_scope_
The implementation of this is virtually identical to the already-existing find_install_
But I changed the implementation of find_install_
It looks like find_scope_
Otherwise, that code is very nicely armor-plated now, thanks!
Just need the test to pass now :-)
- 409. By Marcus Tomlinson
-
Throw SyscallException in DirWatcher constructor to include errno.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:409
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 410. By Marcus Tomlinson
-
Updated find_scope_
dir_configs( ) and find_install_ dir_configs( ) to return maps of name:path. - 411. By Marcus Tomlinson
-
TEST: See if the rm command is causing issues
Michi Henning (michihenning) wrote : | # |
Thanks for the fixes to the exception and the code to locate the .ini files!
Just those tests to sort out now. FWIW, I've just pulled and built your r411 and tried it locally. Works fine in a VM on my laptop.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:410
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 412. By Marcus Tomlinson
-
Use boost::filesystem rather than system() to manipulate files
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:411
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:412
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 413. By Marcus Tomlinson
-
Merged devel and resolved conflicts
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:413
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 414. By Marcus Tomlinson
-
Oops, forgot error_code in filesystem calls
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:414
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 415. By Marcus Tomlinson
-
Upped timeout for wait_for_update
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:415
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 416. By Marcus Tomlinson
-
Replaced nullptr with NULL in select() call
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:416
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 417. By Marcus Tomlinson
-
Merged devel
- 418. By Marcus Tomlinson
-
Added some test logging to find where the "Invalid Argument" exception is coming from
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:418
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 419. By Marcus Tomlinson
-
Added a ton of logging
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:419
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 420. By Marcus Tomlinson
-
Added even more logging
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:420
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 421. By Marcus Tomlinson
-
"set_update_
callback" renamed to "set_list_ update_ callback" to better describe the callback behavior - 422. By Marcus Tomlinson
-
Swap tests around
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:422
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 423. By Marcus Tomlinson
-
Create a RegistryObject:
:SPtr in ScopesWatcher rather than simply referencing it.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:423
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 424. By Marcus Tomlinson
-
Allow only one watch per path in DirWatcher
Marcus Tomlinson (marcustomlinson) wrote : | # |
Michi: ok, so now that Jenkins is happy, could you please have another quick scan through this.
There is a lot of noise in commits while I was trying to fix the test failure, but all thats actually changed since the last review is:
* Replaced system() calls with boost::filesystem in the test.
* Allow only one watch per path in DirWatcher.
* Fixed shared_ptr reference issue.
Michi Henning (michihenning) wrote : | # |
Well done, champagne due now :-)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:424
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'debian/changelog' |
2 | --- debian/changelog 2014-05-23 07:43:20 +0000 |
3 | +++ debian/changelog 2014-05-27 12:46:42 +0000 |
4 | @@ -1,3 +1,13 @@ |
5 | +unity-scopes-api (0.4.8+14.10.20140519-0ubuntu1) UNRELEASED; urgency=medium |
6 | + |
7 | + [ Marcus Tomlinson ] |
8 | + * Introduced Dir/ScopesWatcher classes to watch for updates to the scope install directories. |
9 | + * Updated scoperegistry to inform the registry object when scopes are (un)installed. |
10 | + * Updated all relevant registry classes to propagate an update pub/sub message via middleware. |
11 | + * Added set_update_callback() to Registry (callback executed when the scopes list changes). |
12 | + |
13 | + -- Marcus Tomlinson <marcustomlinson@ubuntu> Thu, 22 May 2014 06:47:28 +0200 |
14 | + |
15 | unity-scopes-api (0.4.8-0ubuntu1) UNRELEASED; urgency=medium |
16 | |
17 | [ Pawel Stolowski ] |
18 | |
19 | === modified file 'debian/libunity-scopes1.symbols' |
20 | --- debian/libunity-scopes1.symbols 2014-05-23 09:32:43 +0000 |
21 | +++ debian/libunity-scopes1.symbols 2014-05-27 12:46:42 +0000 |
22 | @@ -448,7 +448,7 @@ |
23 | (c++)"unity::scopes::internal::RegistryObject::remove_local_scope(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.4.0+14.04.20140312.1 |
24 | (c++)"unity::scopes::internal::RegistryObject::set_remote_registry(std::shared_ptr<unity::scopes::internal::MWRegistry> const&)@Base" 0.4.0+14.04.20140312.1 |
25 | (c++)"unity::scopes::internal::RegistryObject::locate(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.4.0+14.04.20140312.1 |
26 | - (c++)"unity::scopes::internal::RegistryObject::RegistryObject(core::posix::ChildProcess::DeathObserver&, std::shared_ptr<unity::scopes::internal::Executor> const&)@Base" 0.4.3+14.10.20140428 |
27 | + (c++)"unity::scopes::internal::RegistryObject::RegistryObject(core::posix::ChildProcess::DeathObserver&, std::shared_ptr<unity::scopes::internal::Executor> const&, std::shared_ptr<unity::scopes::internal::MiddlewareBase>)@Base" 0replaceme |
28 | (c++)"unity::scopes::internal::RegistryObject::~RegistryObject()@Base" 0.4.0+14.04.20140312.1 |
29 | (c++)"unity::scopes::internal::MiddlewareFactory::MiddlewareData::~MiddlewareData()@Base" 0.4.0+14.04.20140312.1 |
30 | (c++)"unity::scopes::internal::MiddlewareFactory::to_kind(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.4.0+14.04.20140312.1 |
31 | |
32 | === modified file 'include/unity/scopes/Registry.h' |
33 | --- include/unity/scopes/Registry.h 2014-05-19 08:26:40 +0000 |
34 | +++ include/unity/scopes/Registry.h 2014-05-27 12:46:42 +0000 |
35 | @@ -74,6 +74,15 @@ |
36 | */ |
37 | virtual MetadataMap list_if(std::function<bool(ScopeMetadata const& item)> predicate) = 0; |
38 | |
39 | + /** |
40 | + \brief Assigns a callback method to be executed when the registry's scope list changes. |
41 | + |
42 | + Note: Upon receiving this callback, you should retrieve the updated scopes list via the list() method if |
43 | + you wish to retain synchronisation between client and server. |
44 | + \param callback The function object that is invoked when an update occurs. |
45 | + */ |
46 | + virtual void set_list_update_callback(std::function<void()> callback) = 0; |
47 | + |
48 | protected: |
49 | /// @cond |
50 | Registry(); |
51 | |
52 | === modified file 'include/unity/scopes/internal/MWRegistry.h' |
53 | --- include/unity/scopes/internal/MWRegistry.h 2014-04-03 12:57:25 +0000 |
54 | +++ include/unity/scopes/internal/MWRegistry.h 2014-05-27 12:46:42 +0000 |
55 | @@ -20,6 +20,7 @@ |
56 | #define UNITY_SCOPES_INTERNAL_MWREGISTRY_H |
57 | |
58 | #include <unity/scopes/internal/MWObjectProxy.h> |
59 | +#include <unity/scopes/internal/MWSubscriber.h> |
60 | #include <unity/scopes/Registry.h> |
61 | #include <unity/scopes/ScopeMetadata.h> |
62 | |
63 | @@ -42,8 +43,15 @@ |
64 | |
65 | virtual ~MWRegistry(); |
66 | |
67 | + // Local operations |
68 | + void set_list_update_callback(std::function<void()> callback); |
69 | + |
70 | protected: |
71 | MWRegistry(MiddlewareBase* mw_base); |
72 | + |
73 | +private: |
74 | + MiddlewareBase* mw_base_; |
75 | + MWSubscriber::UPtr subscriber_; |
76 | }; |
77 | |
78 | } // namespace internal |
79 | |
80 | === modified file 'include/unity/scopes/internal/RegistryImpl.h' |
81 | --- include/unity/scopes/internal/RegistryImpl.h 2014-05-19 08:28:08 +0000 |
82 | +++ include/unity/scopes/internal/RegistryImpl.h 2014-05-27 12:46:42 +0000 |
83 | @@ -43,6 +43,7 @@ |
84 | virtual ScopeMetadata get_metadata(std::string const& scope_id) override; |
85 | virtual MetadataMap list() override; |
86 | virtual MetadataMap list_if(std::function<bool(ScopeMetadata const& item)> predicate) override; |
87 | + virtual void set_list_update_callback(std::function<void()> callback) override; |
88 | |
89 | // Remote operation. Not part of public API, hence not override. |
90 | ObjectProxy locate(std::string const& identity); |
91 | |
92 | === modified file 'include/unity/scopes/internal/RegistryObject.h' |
93 | --- include/unity/scopes/internal/RegistryObject.h 2014-05-07 04:48:26 +0000 |
94 | +++ include/unity/scopes/internal/RegistryObject.h 2014-05-27 12:46:42 +0000 |
95 | @@ -20,6 +20,8 @@ |
96 | #define UNITY_SCOPES_INTERNAL_REGISTRYOBJECT_H |
97 | |
98 | #include <unity/scopes/internal/Executor.h> |
99 | +#include <unity/scopes/internal/MiddlewareBase.h> |
100 | +#include <unity/scopes/internal/MWPublisher.h> |
101 | #include <unity/scopes/internal/MWRegistryProxyFwd.h> |
102 | #include <unity/scopes/internal/RegistryObjectBase.h> |
103 | #include <unity/scopes/internal/StateReceiverObject.h> |
104 | @@ -55,7 +57,8 @@ |
105 | public: |
106 | UNITY_DEFINES_PTRS(RegistryObject); |
107 | |
108 | - RegistryObject(core::posix::ChildProcess::DeathObserver& death_observer, Executor::SPtr const& executor); |
109 | + RegistryObject(core::posix::ChildProcess::DeathObserver& death_observer, Executor::SPtr const& executor, |
110 | + MiddlewareBase::SPtr middleware); |
111 | virtual ~RegistryObject(); |
112 | |
113 | // Remote operation implementations |
114 | @@ -128,6 +131,8 @@ |
115 | ProcessMap scope_processes_; |
116 | MWRegistryProxy remote_registry_; |
117 | mutable std::mutex mutex_; |
118 | + |
119 | + MWPublisher::UPtr publisher_; |
120 | }; |
121 | |
122 | } // namespace internal |
123 | |
124 | === modified file 'include/unity/scopes/internal/zmq_middleware/ZmqSubscriber.h' |
125 | --- include/unity/scopes/internal/zmq_middleware/ZmqSubscriber.h 2014-05-19 09:17:14 +0000 |
126 | +++ include/unity/scopes/internal/zmq_middleware/ZmqSubscriber.h 2014-05-27 12:46:42 +0000 |
127 | @@ -56,7 +56,7 @@ |
128 | NotRunning, |
129 | Running, |
130 | Stopping, |
131 | - Failed |
132 | + Stopped |
133 | }; |
134 | |
135 | zmqpp::context* const context_; |
136 | |
137 | === modified file 'include/unity/scopes/testing/MockRegistry.h' |
138 | --- include/unity/scopes/testing/MockRegistry.h 2014-05-19 08:31:25 +0000 |
139 | +++ include/unity/scopes/testing/MockRegistry.h 2014-05-27 12:46:42 +0000 |
140 | @@ -43,6 +43,7 @@ |
141 | MOCK_METHOD1(get_metadata, ScopeMetadata(std::string const&)); |
142 | MOCK_METHOD0(list, MetadataMap()); |
143 | MOCK_METHOD1(list_if, MetadataMap(std::function<bool(ScopeMetadata const&)>)); |
144 | + MOCK_METHOD1(set_list_update_callback, void(std::function<void()>)); |
145 | }; |
146 | |
147 | /// @endcond |
148 | |
149 | === modified file 'scoperegistry/CMakeLists.txt' |
150 | --- scoperegistry/CMakeLists.txt 2014-05-19 08:40:28 +0000 |
151 | +++ scoperegistry/CMakeLists.txt 2014-05-27 12:46:42 +0000 |
152 | @@ -1,6 +1,8 @@ |
153 | set(SRC |
154 | + DirWatcher.cpp |
155 | FindFiles.cpp |
156 | scoperegistry.cpp |
157 | + ScopesWatcher.cpp |
158 | ) |
159 | |
160 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) |
161 | |
162 | === added file 'scoperegistry/DirWatcher.cpp' |
163 | --- scoperegistry/DirWatcher.cpp 1970-01-01 00:00:00 +0000 |
164 | +++ scoperegistry/DirWatcher.cpp 2014-05-27 12:46:42 +0000 |
165 | @@ -0,0 +1,272 @@ |
166 | +/* |
167 | + * Copyright (C) 2014 Canonical Ltd |
168 | + * |
169 | + * This program is free software: you can redistribute it and/or modify |
170 | + * it under the terms of the GNU Lesser General Public License version 3 as |
171 | + * published by the Free Software Foundation. |
172 | + * |
173 | + * This program is distributed in the hope that it will be useful, |
174 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
175 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
176 | + * GNU Lesser General Public License for more details. |
177 | + * |
178 | + * You should have received a copy of the GNU Lesser General Public License |
179 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
180 | + * |
181 | + * Authored by: Marcus Tomlinson <marcus.tomlinson@canonical.com> |
182 | + */ |
183 | + |
184 | +#include "DirWatcher.h" |
185 | + |
186 | +#include <unity/UnityExceptions.h> |
187 | + |
188 | +#include <iostream> |
189 | +#include <sys/inotify.h> |
190 | +#include <sys/ioctl.h> |
191 | +#include <unistd.h> |
192 | + |
193 | +using namespace unity; |
194 | + |
195 | +namespace scoperegistry |
196 | +{ |
197 | + |
198 | +DirWatcher::DirWatcher() |
199 | + : fd_(inotify_init()) |
200 | + , thread_state_(Running) |
201 | + , thread_exception_(nullptr) |
202 | +{ |
203 | + // Validate the file descriptor |
204 | + if (fd_ < 0) |
205 | + { |
206 | + throw SyscallException("DirWatcher(): inotify_init() failed on inotify fd (fd = " + |
207 | + std::to_string(fd_) + ")", errno); |
208 | + } |
209 | +} |
210 | + |
211 | +DirWatcher::~DirWatcher() |
212 | +{ |
213 | + { |
214 | + std::lock_guard<std::mutex> lock(mutex_); |
215 | + |
216 | + if (thread_state_ == Failed) |
217 | + { |
218 | + try |
219 | + { |
220 | + std::rethrow_exception(thread_exception_); |
221 | + } |
222 | + catch (std::exception const& e) |
223 | + { |
224 | + std::cerr << "~DirWatcher(): " << e.what() << std::endl; |
225 | + } |
226 | + catch (...) |
227 | + { |
228 | + std::cerr << "~DirWatcher(): watch_thread was aborted due to an unknown exception" |
229 | + << std::endl; |
230 | + } |
231 | + } |
232 | + else |
233 | + { |
234 | + // Set state to Stopping |
235 | + thread_state_ = Stopping; |
236 | + |
237 | + // Remove watches (causes read to return) |
238 | + for (auto& wd : wds_) |
239 | + { |
240 | + inotify_rm_watch(fd_, wd.first); |
241 | + } |
242 | + wds_.clear(); |
243 | + } |
244 | + } |
245 | + |
246 | + // Wait for thread to terminate |
247 | + if (thread_.joinable()) |
248 | + { |
249 | + thread_.join(); |
250 | + } |
251 | + |
252 | + // Close the file descriptor |
253 | + close(fd_); |
254 | +} |
255 | + |
256 | +void DirWatcher::add_watch(std::string const& path) |
257 | +{ |
258 | + std::lock_guard<std::mutex> lock(mutex_); |
259 | + |
260 | + if (thread_state_ == Failed) |
261 | + { |
262 | + std::rethrow_exception(thread_exception_); |
263 | + } |
264 | + |
265 | + for (auto const& wd : wds_) |
266 | + { |
267 | + if (wd.second == path) |
268 | + { |
269 | + throw ResourceException("DirWatcher::add_watch(): failed to add watch for path: \"" + |
270 | + path + "\". Watch already exists."); |
271 | + } |
272 | + } |
273 | + |
274 | + int wd = inotify_add_watch(fd_, path.c_str(), IN_CREATE | IN_MOVED_TO | |
275 | + IN_DELETE | IN_MOVED_FROM | |
276 | + IN_MODIFY | IN_ATTRIB); |
277 | + if (wd < 0) |
278 | + { |
279 | + throw ResourceException("DirWatcher::add_watch(): failed to add watch for path: \"" + |
280 | + path + "\". inotify_add_watch() failed. (fd = " + |
281 | + std::to_string(fd_) + ", path = " + path + ")"); |
282 | + } |
283 | + |
284 | + wds_[wd] = path; |
285 | + |
286 | + // If this is the first watch, start the thread |
287 | + if (wds_.size() == 1) |
288 | + { |
289 | + thread_ = std::thread(&DirWatcher::watch_thread, this); |
290 | + } |
291 | +} |
292 | + |
293 | +void DirWatcher::remove_watch(std::string const& path) |
294 | +{ |
295 | + std::lock_guard<std::mutex> lock(mutex_); |
296 | + |
297 | + if (thread_state_ == Failed) |
298 | + { |
299 | + std::rethrow_exception(thread_exception_); |
300 | + } |
301 | + |
302 | + for (auto const& wd : wds_) |
303 | + { |
304 | + if (wd.second == path) |
305 | + { |
306 | + // If this is the last watch, stop the thread |
307 | + if (wds_.size() == 1) |
308 | + { |
309 | + thread_state_ = Stopping; |
310 | + } |
311 | + |
312 | + // Remove watch (causes read to return) |
313 | + inotify_rm_watch(fd_, wd.first); |
314 | + wds_.erase(wd.first); |
315 | + break; |
316 | + } |
317 | + } |
318 | +} |
319 | + |
320 | +void DirWatcher::watch_thread() |
321 | +{ |
322 | + try |
323 | + { |
324 | + fd_set fds; |
325 | + FD_ZERO(&fds); |
326 | + FD_SET(fd_, &fds); |
327 | + |
328 | + int bytes_avail = 0; |
329 | + std::string buffer; |
330 | + std::string event_path; |
331 | + |
332 | + // Poll for notifications until stop is requested |
333 | + while (true) |
334 | + { |
335 | + // Wait for a payload to arrive |
336 | + int ret = select(fd_ + 1, &fds, nullptr, nullptr, nullptr); |
337 | + if (ret < 0) |
338 | + { |
339 | + throw SyscallException("DirWatcher::watch_thread(): Thread aborted: " |
340 | + "select() failed on inotify fd (fd = " + |
341 | + std::to_string(fd_) + ")", errno); |
342 | + } |
343 | + // Get number of bytes available |
344 | + ret = ioctl(fd_, FIONREAD, &bytes_avail); |
345 | + if (ret < 0) |
346 | + { |
347 | + throw SyscallException("DirWatcher::watch_thread(): Thread aborted: " |
348 | + "ioctl() failed on inotify fd (fd = " + |
349 | + std::to_string(fd_) + ")", errno); |
350 | + } |
351 | + // Read available bytes |
352 | + buffer.resize(bytes_avail); |
353 | + int bytes_read = read(fd_, &buffer[0], buffer.size()); |
354 | + if (bytes_read < 0) |
355 | + { |
356 | + throw SyscallException("DirWatcher::watch_thread(): Thread aborted: " |
357 | + "read() failed on inotify fd (fd = " + |
358 | + std::to_string(fd_) + ")", errno); |
359 | + } |
360 | + |
361 | + // Process event(s) received |
362 | + int i = 0; |
363 | + while (i < bytes_read) |
364 | + { |
365 | + struct inotify_event* event = (inotify_event*)&buffer[i]; |
366 | + { |
367 | + event_path = ""; |
368 | + std::lock_guard<std::mutex> lock(mutex_); |
369 | + if (wds_.find(event->wd) != wds_.end()) |
370 | + { |
371 | + event_path = wds_.at(event->wd) + "/" + event->name; |
372 | + } |
373 | + } |
374 | + |
375 | + if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) |
376 | + { |
377 | + if (event->mask & IN_ISDIR) |
378 | + { |
379 | + watch_event(Added, Directory, event_path); |
380 | + } |
381 | + else |
382 | + { |
383 | + watch_event(Added, File, event_path); |
384 | + } |
385 | + } |
386 | + else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) |
387 | + { |
388 | + if (event->mask & IN_ISDIR) |
389 | + { |
390 | + watch_event(Removed, Directory, event_path); |
391 | + } |
392 | + else |
393 | + { |
394 | + watch_event(Removed, File, event_path); |
395 | + } |
396 | + } |
397 | + else if (event->mask & IN_MODIFY || event->mask & IN_ATTRIB) |
398 | + { |
399 | + if (event->mask & IN_ISDIR) |
400 | + { |
401 | + watch_event(Modified, Directory, event_path); |
402 | + } |
403 | + else |
404 | + { |
405 | + watch_event(Modified, File, event_path); |
406 | + } |
407 | + } |
408 | + i += sizeof(inotify_event) + event->len; |
409 | + } |
410 | + |
411 | + // Break from the loop if we are stopping |
412 | + { |
413 | + std::lock_guard<std::mutex> lock(mutex_); |
414 | + if (thread_state_ == Stopping) |
415 | + { |
416 | + break; |
417 | + } |
418 | + } |
419 | + } |
420 | + } |
421 | + catch (std::exception const& e) |
422 | + { |
423 | + std::cerr << e.what() << std::endl; |
424 | + std::lock_guard<std::mutex> lock(mutex_); |
425 | + thread_state_ = Failed; |
426 | + thread_exception_ = std::current_exception(); |
427 | + } |
428 | + catch (...) |
429 | + { |
430 | + std::cerr << "DirWatcher::watch_thread(): Thread aborted: unknown exception" << std::endl; |
431 | + std::lock_guard<std::mutex> lock(mutex_); |
432 | + thread_state_ = Failed; |
433 | + thread_exception_ = std::current_exception(); |
434 | + } |
435 | +} |
436 | + |
437 | +} // namespace scoperegistry |
438 | |
439 | === added file 'scoperegistry/DirWatcher.h' |
440 | --- scoperegistry/DirWatcher.h 1970-01-01 00:00:00 +0000 |
441 | +++ scoperegistry/DirWatcher.h 2014-05-27 12:46:42 +0000 |
442 | @@ -0,0 +1,79 @@ |
443 | +/* |
444 | + * Copyright (C) 2014 Canonical Ltd |
445 | + * |
446 | + * This program is free software: you can redistribute it and/or modify |
447 | + * it under the terms of the GNU Lesser General Public License version 3 as |
448 | + * published by the Free Software Foundation. |
449 | + * |
450 | + * This program is distributed in the hope that it will be useful, |
451 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
452 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
453 | + * GNU Lesser General Public License for more details. |
454 | + * |
455 | + * You should have received a copy of the GNU Lesser General Public License |
456 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
457 | + * |
458 | + * Authored by: Marcus Tomlinson <marcus.tomlinson@canonical.com> |
459 | + */ |
460 | + |
461 | +#ifndef SCOPEREGISTRY_DIRWATCHER_H |
462 | +#define SCOPEREGISTRY_DIRWATCHER_H |
463 | + |
464 | +#include <condition_variable> |
465 | +#include <map> |
466 | +#include <thread> |
467 | + |
468 | +namespace scoperegistry |
469 | +{ |
470 | + |
471 | +// DirWatcher watches directories specified by calls to add_watch() / remove_watch() for changes in |
472 | +// the files and folders contained. If a file or folder is added, removed or modified, the pure |
473 | +// virtual watch_event() method is executed (this is to be overridden by a child class deriving |
474 | +// from DirWatcher) |
475 | + |
476 | +class DirWatcher |
477 | +{ |
478 | +public: |
479 | + enum EventType |
480 | + { |
481 | + Added, |
482 | + Removed, |
483 | + Modified |
484 | + }; |
485 | + |
486 | + enum FileType |
487 | + { |
488 | + File, |
489 | + Directory |
490 | + }; |
491 | + |
492 | + DirWatcher(); |
493 | + ~DirWatcher(); |
494 | + |
495 | + void add_watch(std::string const& path); |
496 | + void remove_watch(std::string const& path); |
497 | + |
498 | +private: |
499 | + enum ThreadState |
500 | + { |
501 | + Running, |
502 | + Stopping, |
503 | + Failed |
504 | + }; |
505 | + |
506 | + int const fd_; |
507 | + |
508 | + std::map<int, std::string> wds_; |
509 | + |
510 | + std::thread thread_; |
511 | + std::mutex mutex_; |
512 | + ThreadState thread_state_; |
513 | + std::exception_ptr thread_exception_; |
514 | + |
515 | + void watch_thread(); |
516 | + virtual void watch_event(EventType event_type, FileType file_type, std::string const& path) = 0; |
517 | +}; |
518 | + |
519 | +} // namespace scoperegistry |
520 | + |
521 | +#endif // SCOPEREGISTRY_DIRWATCHER_H |
522 | |
523 | === modified file 'scoperegistry/FindFiles.cpp' |
524 | --- scoperegistry/FindFiles.cpp 2014-05-21 02:07:16 +0000 |
525 | +++ scoperegistry/FindFiles.cpp 2014-05-27 12:46:42 +0000 |
526 | @@ -36,14 +36,9 @@ |
527 | namespace scoperegistry |
528 | { |
529 | |
530 | -namespace |
531 | -{ |
532 | - |
533 | // Return all paths underneath the given dir that are of the given type |
534 | // or are a symbolic link. |
535 | |
536 | -enum EntryType { File, Directory }; |
537 | - |
538 | vector<string> find_entries(string const& install_dir, EntryType type) |
539 | { |
540 | DIR* d = opendir(install_dir.c_str()); |
541 | @@ -83,39 +78,56 @@ |
542 | return entries; |
543 | } |
544 | |
545 | -} // namespace |
546 | +// Return all files of the form dir/<scomescope>.ini that are regular files or |
547 | +// symbolic links and have the specified suffix. |
548 | +// The empty suffix is legal and causes all regular files and symlinks to be returned. |
549 | + |
550 | +map<string, string> find_scope_dir_configs(string const& scope_dir, string const& suffix) |
551 | +{ |
552 | + map<string, string> files; |
553 | + |
554 | + auto paths = find_entries(scope_dir, File); |
555 | + for (auto path : paths) |
556 | + { |
557 | + filesystem::path fs_path(path); |
558 | + if (fs_path.extension() != suffix) |
559 | + { |
560 | + continue; |
561 | + } |
562 | + auto scope_id = fs_path.stem().native(); |
563 | + files.insert(make_pair(scope_id, path)); |
564 | + } |
565 | + |
566 | + return files; |
567 | +} |
568 | |
569 | // Return all files of the form dir/*/<scomescope>.ini that are regular files or |
570 | // symbolic links and have the specified suffix. |
571 | // The empty suffix is legal and causes all regular files and symlinks to be returned. |
572 | // Print error message for any scopes with an id that was seen previously. |
573 | |
574 | -vector<string> find_scope_config_files(string const& install_dir, |
575 | - string const& suffix, |
576 | - function<void(string const&)> error) |
577 | +map<string, string> find_install_dir_configs(string const& install_dir, |
578 | + string const& suffix, |
579 | + function<void(string const&)> error) |
580 | { |
581 | - vector<string> files; |
582 | + map<string, string> files; |
583 | map<string, string> scopes_seen; |
584 | |
585 | - auto subdirs = find_entries(install_dir, Directory); |
586 | - for (auto subdir : subdirs) |
587 | + auto scope_dirs = find_entries(install_dir, Directory); |
588 | + for (auto scope_dir : scope_dirs) |
589 | { |
590 | - auto candidates = find_entries(subdir, File); |
591 | - for (auto c : candidates) |
592 | + auto configs = find_scope_dir_configs(scope_dir, suffix); |
593 | + for (auto config : configs) |
594 | { |
595 | - filesystem::path path(c); |
596 | - if (path.extension() != suffix) { |
597 | - continue; |
598 | - } |
599 | - auto stem = path.stem().native(); |
600 | - auto const it = scopes_seen.find(stem); |
601 | + auto const it = scopes_seen.find(config.first); |
602 | if (it != scopes_seen.end()) |
603 | { |
604 | - error("ignoring second instance of non-unique scope: " + path.native() + "\n" |
605 | + error("ignoring second instance of non-unique scope: " + config.second + "\n" |
606 | "previous instance: " + it->second); |
607 | + continue; |
608 | } |
609 | - scopes_seen[stem] = path.native(); |
610 | - files.emplace_back(c); |
611 | + scopes_seen[config.first] = config.second; |
612 | + files.insert(config); |
613 | } |
614 | } |
615 | |
616 | |
617 | === modified file 'scoperegistry/FindFiles.h' |
618 | --- scoperegistry/FindFiles.h 2014-05-21 02:07:16 +0000 |
619 | +++ scoperegistry/FindFiles.h 2014-05-27 12:46:42 +0000 |
620 | @@ -21,12 +21,25 @@ |
621 | |
622 | #include <functional> |
623 | #include <string> |
624 | +#include <map> |
625 | #include <vector> |
626 | |
627 | namespace scoperegistry |
628 | { |
629 | |
630 | -// Return a vector of file names underneath a scope root install dir that have the given suffix. |
631 | +// Return a vector of all paths found underneath a given dir that are of the given type or are |
632 | +// a symbolic link. |
633 | + |
634 | +enum EntryType { File, Directory }; |
635 | +std::vector<std::string> find_entries(std::string const& install_dir, EntryType type); |
636 | + |
637 | +// Return a map of file names:paths underneath a scope dir that have the given suffix. Files are |
638 | +// searched for in the specified directory only, that is, no .ini files in further-nested |
639 | +// directories will be searched. |
640 | + |
641 | +std::map<std::string, std::string> find_scope_dir_configs(std::string const& scope_dir, std::string const& suffix); |
642 | + |
643 | +// Return a map of file names:paths underneath a scope root install dir that have the given suffix. |
644 | // Files are searched for exactly "one level down", that is, if we have a directory structure. |
645 | // |
646 | // canonical/scopeA/myconfig.ini |
647 | @@ -35,14 +48,9 @@ |
648 | // we get those two .ini files, but no .ini files in canonical or underneath |
649 | // further-nested directories. |
650 | |
651 | -std::vector<std::string> find_scope_config_files(std::string const& install_dir, |
652 | - std::string const& suffix, |
653 | - std::function<void(std::string const&)> error); |
654 | - |
655 | -// Return a vector of file names in dir with the given suffix. |
656 | - |
657 | -std::vector<std::string> find_files(std::string const& dir, |
658 | - std::string const& suffix); |
659 | +std::map<std::string, std::string> find_install_dir_configs(std::string const& install_dir, |
660 | + std::string const& suffix, |
661 | + std::function<void(std::string const&)> error); |
662 | |
663 | } // namespace scoperegistry |
664 | |
665 | |
666 | === added file 'scoperegistry/ScopesWatcher.cpp' |
667 | --- scoperegistry/ScopesWatcher.cpp 1970-01-01 00:00:00 +0000 |
668 | +++ scoperegistry/ScopesWatcher.cpp 2014-05-27 12:46:42 +0000 |
669 | @@ -0,0 +1,184 @@ |
670 | +/* |
671 | + * Copyright (C) 2014 Canonical Ltd |
672 | + * |
673 | + * This program is free software: you can redistribute it and/or modify |
674 | + * it under the terms of the GNU Lesser General Public License version 3 as |
675 | + * published by the Free Software Foundation. |
676 | + * |
677 | + * This program is distributed in the hope that it will be useful, |
678 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
679 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
680 | + * GNU Lesser General Public License for more details. |
681 | + * |
682 | + * You should have received a copy of the GNU Lesser General Public License |
683 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
684 | + * |
685 | + * Authored by: Marcus Tomlinson <marcus.tomlinson@canonical.com> |
686 | + */ |
687 | + |
688 | +#include "ScopesWatcher.h" |
689 | + |
690 | +#include "FindFiles.h" |
691 | + |
692 | +#include <boost/filesystem/path.hpp> |
693 | + |
694 | +using namespace unity::scopes::internal; |
695 | +using namespace boost; |
696 | + |
697 | +namespace scoperegistry |
698 | +{ |
699 | + |
700 | +ScopesWatcher::ScopesWatcher(RegistryObject::SPtr registry, |
701 | + std::function<void(std::pair<std::string, std::string> const&)> ini_added_callback) |
702 | + : registry_(registry) |
703 | + , ini_added_callback_(ini_added_callback) |
704 | +{ |
705 | +} |
706 | + |
707 | +void ScopesWatcher::add_install_dir(std::string const& dir) |
708 | +{ |
709 | + try |
710 | + { |
711 | + // Add watch for root directory |
712 | + add_watch(dir); |
713 | + |
714 | + // Add watches for each sub directory in root |
715 | + auto subdirs = find_entries(dir, EntryType::Directory); |
716 | + for (auto const& subdir : subdirs) |
717 | + { |
718 | + auto configs = find_scope_dir_configs(subdir, ".ini"); |
719 | + if (!configs.empty()) |
720 | + { |
721 | + auto config = *configs.cbegin(); |
722 | + std::lock_guard<std::mutex> lock(mutex_); |
723 | + dir_to_ini_map_[subdir] = config.second; |
724 | + } |
725 | + add_watch(subdir); |
726 | + } |
727 | + } |
728 | + catch (...) {} |
729 | +} |
730 | + |
731 | +void ScopesWatcher::add_scope_dir(std::string const& dir) |
732 | +{ |
733 | + auto configs = find_scope_dir_configs(dir, ".ini"); |
734 | + if (!configs.empty()) |
735 | + { |
736 | + auto config = *configs.cbegin(); |
737 | + // Associate this directory with the contained config file |
738 | + { |
739 | + std::lock_guard<std::mutex> lock(mutex_); |
740 | + dir_to_ini_map_[dir] = config.second; |
741 | + } |
742 | + |
743 | + // New config found, execute callback |
744 | + ini_added_callback_(config); |
745 | + std::cout << "ScopesWatcher: scope: \"" << config.first << "\" installed to: \"" |
746 | + << dir << "\"" << std::endl; |
747 | + } |
748 | + |
749 | + // Add a watch for this directory |
750 | + add_watch(dir); |
751 | +} |
752 | + |
753 | +void ScopesWatcher::remove_scope_dir(std::string const& dir) |
754 | +{ |
755 | + std::lock_guard<std::mutex> lock(mutex_); |
756 | + |
757 | + // Check if this directory is associate with the config file |
758 | + if (dir_to_ini_map_.find(dir) != dir_to_ini_map_.end()) |
759 | + { |
760 | + // Inform the registry that this scope has been removed |
761 | + std::string ini_path = dir_to_ini_map_.at(dir); |
762 | + dir_to_ini_map_.erase(dir); |
763 | + |
764 | + filesystem::path p(ini_path); |
765 | + std::string scope_id = p.stem().native(); |
766 | + registry_->remove_local_scope(scope_id); |
767 | + std::cout << "ScopesWatcher: scope: \"" << scope_id << "\" uninstalled from: \"" |
768 | + << dir << "\"" << std::endl; |
769 | + } |
770 | + |
771 | + // Remove the watch for this directory |
772 | + remove_watch(dir); |
773 | +} |
774 | + |
775 | +void ScopesWatcher::watch_event(DirWatcher::EventType event_type, |
776 | + DirWatcher::FileType file_type, |
777 | + std::string const& path) |
778 | +{ |
779 | + filesystem::path fs_path(path); |
780 | + |
781 | + if (file_type == DirWatcher::File && fs_path.extension() == ".ini") |
782 | + { |
783 | + std::lock_guard<std::mutex> lock(mutex_); |
784 | + std::string parent_path = fs_path.parent_path().native(); |
785 | + std::string scope_id = fs_path.stem().native(); |
786 | + |
787 | + // A .ini has been added / modified |
788 | + if (event_type == DirWatcher::Added || event_type == DirWatcher::Modified) |
789 | + { |
790 | + dir_to_ini_map_[parent_path] = path; |
791 | + ini_added_callback_(std::make_pair(scope_id, path)); |
792 | + std::cout << "ScopesWatcher: scope: \"" << scope_id << "\" installed to: \"" |
793 | + << parent_path << "\"" << std::endl; |
794 | + } |
795 | + // A .ini has been removed |
796 | + else if (event_type == DirWatcher::Removed) |
797 | + { |
798 | + dir_to_ini_map_.erase(parent_path); |
799 | + registry_->remove_local_scope(scope_id); |
800 | + std::cout << "ScopesWatcher: scope: \"" << scope_id << "\" uninstalled from: \"" |
801 | + << parent_path << "\"" << std::endl; |
802 | + } |
803 | + } |
804 | + else if (file_type == DirWatcher::File && fs_path.extension() == ".so") |
805 | + { |
806 | + std::lock_guard<std::mutex> lock(mutex_); |
807 | + std::string parent_path = fs_path.parent_path().native(); |
808 | + |
809 | + // Check if this directory is associate with the config file |
810 | + if (dir_to_ini_map_.find(parent_path) != dir_to_ini_map_.end()) |
811 | + { |
812 | + std::string ini_path = dir_to_ini_map_.at(parent_path); |
813 | + filesystem::path fs_ini_path(ini_path); |
814 | + std::string scope_id = fs_ini_path.stem().native(); |
815 | + |
816 | + // A .so file has been added / modified |
817 | + if (event_type == DirWatcher::Added || event_type == DirWatcher::Modified) |
818 | + { |
819 | + ini_added_callback_(std::make_pair(scope_id, ini_path)); |
820 | + std::cout << "ScopesWatcher: scope: \"" << scope_id << "\" installed to: \"" |
821 | + << parent_path << "\"" << std::endl; |
822 | + } |
823 | + // A .so file has been removed |
824 | + else if (event_type == DirWatcher::Removed) |
825 | + { |
826 | + registry_->remove_local_scope(scope_id); |
827 | + std::cout << "ScopesWatcher: scope: \"" << scope_id << "\" uninstalled from: \"" |
828 | + << parent_path << "\"" << std::endl; |
829 | + } |
830 | + } |
831 | + } |
832 | + else |
833 | + { |
834 | + // A new sub directory has been added |
835 | + if (event_type == DirWatcher::Added) |
836 | + { |
837 | + // try add this path as a scope folder |
838 | + // (we need to do this with both files and folders added, as the file added may be a symlink) |
839 | + try |
840 | + { |
841 | + add_scope_dir(path); |
842 | + } |
843 | + catch (...) {} |
844 | + } |
845 | + // A sub directory has been removed |
846 | + else if (event_type == DirWatcher::Removed) |
847 | + { |
848 | + remove_scope_dir(path); |
849 | + } |
850 | + } |
851 | +} |
852 | + |
853 | +} // namespace scoperegistry |
854 | |
855 | === added file 'scoperegistry/ScopesWatcher.h' |
856 | --- scoperegistry/ScopesWatcher.h 1970-01-01 00:00:00 +0000 |
857 | +++ scoperegistry/ScopesWatcher.h 2014-05-27 12:46:42 +0000 |
858 | @@ -0,0 +1,57 @@ |
859 | +/* |
860 | + * Copyright (C) 2014 Canonical Ltd |
861 | + * |
862 | + * This program is free software: you can redistribute it and/or modify |
863 | + * it under the terms of the GNU Lesser General Public License version 3 as |
864 | + * published by the Free Software Foundation. |
865 | + * |
866 | + * This program is distributed in the hope that it will be useful, |
867 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
868 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
869 | + * GNU Lesser General Public License for more details. |
870 | + * |
871 | + * You should have received a copy of the GNU Lesser General Public License |
872 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
873 | + * |
874 | + * Authored by: Marcus Tomlinson <marcus.tomlinson@canonical.com> |
875 | + */ |
876 | + |
877 | +#ifndef SCOPEREGISTRY_SCOPESWATCHER_H |
878 | +#define SCOPEREGISTRY_SCOPESWATCHER_H |
879 | + |
880 | +#include <DirWatcher.h> |
881 | + |
882 | +#include <unity/scopes/internal/RegistryObject.h> |
883 | + |
884 | +namespace scoperegistry |
885 | +{ |
886 | + |
887 | +// ScopesWatcher watches the scope install directories specified by calls to add_install_dir() for |
888 | +// the installation / uninstallation of scopes. If a scope is removed, the registry is informed |
889 | +// accordingly. If a scope is added, a user callback (provided on construction) is executed. |
890 | + |
891 | +class ScopesWatcher : public DirWatcher |
892 | +{ |
893 | +public: |
894 | + ScopesWatcher(unity::scopes::internal::RegistryObject::SPtr registry, |
895 | + std::function<void(std::pair<std::string, std::string> const&)> ini_added_callback); |
896 | + |
897 | + void add_install_dir(std::string const& dir); |
898 | + |
899 | +private: |
900 | + unity::scopes::internal::RegistryObject::SPtr const registry_; |
901 | + std::function<void(std::pair<std::string, std::string> const&)> const ini_added_callback_; |
902 | + std::map<std::string, std::string> dir_to_ini_map_; |
903 | + std::mutex mutex_; |
904 | + |
905 | + void add_scope_dir(std::string const& dir); |
906 | + void remove_scope_dir(std::string const& dir); |
907 | + |
908 | + void watch_event(DirWatcher::EventType event_type, |
909 | + DirWatcher::FileType file_type, |
910 | + std::string const& path) override; |
911 | +}; |
912 | + |
913 | +} // namespace scoperegistry |
914 | + |
915 | +#endif // SCOPEREGISTRY_SCOPESWATCHER_H |
916 | |
917 | === modified file 'scoperegistry/scoperegistry.cpp' |
918 | --- scoperegistry/scoperegistry.cpp 2014-05-21 02:07:16 +0000 |
919 | +++ scoperegistry/scoperegistry.cpp 2014-05-27 12:46:42 +0000 |
920 | @@ -17,6 +17,7 @@ |
921 | */ |
922 | |
923 | #include "FindFiles.h" |
924 | +#include "ScopesWatcher.h" |
925 | |
926 | #include <unity/scopes/internal/MiddlewareFactory.h> |
927 | #include <unity/scopes/internal/MWRegistry.h> |
928 | @@ -136,26 +137,24 @@ |
929 | map<string, string> fixed_scopes; // Scopes that the OEM cannot override |
930 | map<string, string> overrideable_scopes; // Scopes that the OEM can override |
931 | |
932 | - auto config_files = find_scope_config_files(scope_installdir, ".ini", error); |
933 | + auto config_files = find_install_dir_configs(scope_installdir, ".ini", error); |
934 | for (auto&& path : config_files) |
935 | { |
936 | - filesystem::path p(path); |
937 | - string scope_id = p.stem().native(); |
938 | try |
939 | { |
940 | - ScopeConfig config(path); |
941 | + ScopeConfig config(path.second); |
942 | if (config.overrideable()) |
943 | { |
944 | - overrideable_scopes[scope_id] = path; |
945 | + overrideable_scopes[path.first] = path.second; |
946 | } |
947 | else |
948 | { |
949 | - fixed_scopes[scope_id] = path; |
950 | + fixed_scopes[path.first] = path.second; |
951 | } |
952 | } |
953 | catch (unity::Exception const& e) |
954 | { |
955 | - error("ignoring scope \"" + scope_id + "\": configuration error:\n" + e.what()); |
956 | + error("ignoring scope \"" + path.first + "\": configuration error:\n" + e.what()); |
957 | } |
958 | } |
959 | |
960 | @@ -163,19 +162,16 @@ |
961 | { |
962 | try |
963 | { |
964 | - auto oem_paths = find_scope_config_files(oem_installdir, ".ini", error); |
965 | + auto oem_paths = find_install_dir_configs(oem_installdir, ".ini", error); |
966 | for (auto&& path : oem_paths) |
967 | { |
968 | - filesystem::path p(path); |
969 | - string file_name = p.filename().native(); |
970 | - string scope_id = p.stem().native(); |
971 | - if (fixed_scopes.find(scope_id) == fixed_scopes.end()) |
972 | + if (fixed_scopes.find(path.first) == fixed_scopes.end()) |
973 | { |
974 | - overrideable_scopes[scope_id] = path; // Replaces scope if it was present already |
975 | + overrideable_scopes[path.first] = path.second; // Replaces scope if it was present already |
976 | } |
977 | else |
978 | { |
979 | - error("ignoring non-overrideable scope config \"" + file_name + "\" in OEM directory " + oem_installdir); |
980 | + error("ignoring non-overrideable scope config \"" + path.second + "\" in OEM directory " + oem_installdir); |
981 | } |
982 | } |
983 | } |
984 | @@ -199,19 +195,16 @@ |
985 | { |
986 | try |
987 | { |
988 | - auto click_paths = find_scope_config_files(click_installdir, ".ini", error); |
989 | + auto click_paths = find_install_dir_configs(click_installdir, ".ini", error); |
990 | for (auto&& path : click_paths) |
991 | { |
992 | - filesystem::path p(path); |
993 | - string file_name = p.filename().native(); |
994 | - string scope_id = p.stem().native(); |
995 | - if (local_scopes.find(scope_id) == local_scopes.end()) |
996 | + if (local_scopes.find(path.first) == local_scopes.end()) |
997 | { |
998 | - click_scopes[scope_id] = path; |
999 | + click_scopes[path.first] = path.second; |
1000 | } |
1001 | else |
1002 | { |
1003 | - error("ignoring non-overrideable scope config \"" + file_name + "\" in click directory " + click_installdir); |
1004 | + error("ignoring non-overrideable scope config \"" + path.second + "\" in click directory " + click_installdir); |
1005 | } |
1006 | } |
1007 | } |
1008 | @@ -228,6 +221,94 @@ |
1009 | // For each scope, open the config file for the scope, create the metadata info from the config, |
1010 | // and add an entry to the RegistryObject. |
1011 | |
1012 | +void add_local_scope(RegistryObject::SPtr const& registry, |
1013 | + pair<string, string> const& scope, |
1014 | + MiddlewareBase::SPtr const& mw, |
1015 | + string const& scoperunner_path, |
1016 | + string const& config_file, |
1017 | + bool click, |
1018 | + int timeout_ms) |
1019 | +{ |
1020 | + unique_ptr<ScopeMetadataImpl> mi(new ScopeMetadataImpl(mw.get())); |
1021 | + string scope_config(scope.second); |
1022 | + ScopeConfig sc(scope_config); |
1023 | + |
1024 | + filesystem::path scope_path(scope_config); |
1025 | + filesystem::path scope_dir(scope_path.parent_path()); |
1026 | + |
1027 | + mi->set_scope_id(scope.first); |
1028 | + mi->set_display_name(sc.display_name()); |
1029 | + mi->set_description(sc.description()); |
1030 | + mi->set_author(sc.author()); |
1031 | + mi->set_invisible(sc.invisible()); |
1032 | + mi->set_appearance_attributes(sc.appearance_attributes()); |
1033 | + mi->set_scope_directory(scope_dir.native()); |
1034 | + mi->set_results_ttl_type(sc.results_ttl_type()); |
1035 | + |
1036 | + try |
1037 | + { |
1038 | + mi->set_art(relative_scope_path_to_abs_path(sc.art(), scope_dir).native()); |
1039 | + } |
1040 | + catch (NotFoundException const&) |
1041 | + { |
1042 | + } |
1043 | + try |
1044 | + { |
1045 | + mi->set_icon(relative_scope_path_to_abs_path(sc.icon(), scope_dir).native()); |
1046 | + } |
1047 | + catch (NotFoundException const&) |
1048 | + { |
1049 | + } |
1050 | + try |
1051 | + { |
1052 | + mi->set_search_hint(sc.search_hint()); |
1053 | + } |
1054 | + catch (NotFoundException const&) |
1055 | + { |
1056 | + } |
1057 | + try |
1058 | + { |
1059 | + mi->set_hot_key(sc.hot_key()); |
1060 | + } |
1061 | + catch (NotFoundException const&) |
1062 | + { |
1063 | + } |
1064 | + |
1065 | + ScopeProxy proxy = ScopeImpl::create(mw->create_scope_proxy(scope.first), mw->runtime(), scope.first); |
1066 | + mi->set_proxy(proxy); |
1067 | + auto meta = ScopeMetadataImpl::create(move(mi)); |
1068 | + |
1069 | + RegistryObject::ScopeExecData exec_data; |
1070 | + exec_data.scope_id = scope.first; |
1071 | + // get custom scope runner executable, if not set use default scoperunner |
1072 | + exec_data.scoperunner_path = scoperunner_path; |
1073 | + |
1074 | + if (click) |
1075 | + { |
1076 | + exec_data.confinement_profile = |
1077 | + scope_path.parent_path().filename().native(); |
1078 | + } |
1079 | + |
1080 | + exec_data.timeout_ms = timeout_ms; |
1081 | + |
1082 | + try |
1083 | + { |
1084 | + auto custom_exec = sc.scope_runner(); |
1085 | + if (custom_exec.empty()) |
1086 | + { |
1087 | + throw unity::InvalidArgumentException("Invalid scope runner executable for scope: " + scope.first); |
1088 | + } |
1089 | + exec_data.scoperunner_path = relative_scope_path_to_abs_path(custom_exec, scope_dir).native(); |
1090 | + } |
1091 | + catch (NotFoundException const&) |
1092 | + { |
1093 | + } |
1094 | + exec_data.runtime_config = config_file; |
1095 | + exec_data.scope_config = scope.second; |
1096 | + |
1097 | + registry->add_local_scope(scope.first, move(meta), exec_data); |
1098 | +} |
1099 | + |
1100 | void add_local_scopes(RegistryObject::SPtr const& registry, |
1101 | map<string, string> const& all_scopes, |
1102 | MiddlewareBase::SPtr const& mw, |
1103 | @@ -240,84 +321,7 @@ |
1104 | { |
1105 | try |
1106 | { |
1107 | - unique_ptr<ScopeMetadataImpl> mi(new ScopeMetadataImpl(mw.get())); |
1108 | - string scope_config(pair.second); |
1109 | - ScopeConfig sc(scope_config); |
1110 | - |
1111 | - filesystem::path scope_path(scope_config); |
1112 | - filesystem::path scope_dir(scope_path.parent_path()); |
1113 | - |
1114 | - mi->set_scope_id(pair.first); |
1115 | - mi->set_display_name(sc.display_name()); |
1116 | - mi->set_description(sc.description()); |
1117 | - mi->set_author(sc.author()); |
1118 | - mi->set_invisible(sc.invisible()); |
1119 | - mi->set_appearance_attributes(sc.appearance_attributes()); |
1120 | - mi->set_scope_directory(scope_dir.native()); |
1121 | - mi->set_results_ttl_type(sc.results_ttl_type()); |
1122 | - |
1123 | - try |
1124 | - { |
1125 | - mi->set_art(relative_scope_path_to_abs_path(sc.art(), scope_dir).native()); |
1126 | - } |
1127 | - catch (NotFoundException const&) |
1128 | - { |
1129 | - } |
1130 | - try |
1131 | - { |
1132 | - mi->set_icon(relative_scope_path_to_abs_path(sc.icon(), scope_dir).native()); |
1133 | - } |
1134 | - catch (NotFoundException const&) |
1135 | - { |
1136 | - } |
1137 | - try |
1138 | - { |
1139 | - mi->set_search_hint(sc.search_hint()); |
1140 | - } |
1141 | - catch (NotFoundException const&) |
1142 | - { |
1143 | - } |
1144 | - try |
1145 | - { |
1146 | - mi->set_hot_key(sc.hot_key()); |
1147 | - } |
1148 | - catch (NotFoundException const&) |
1149 | - { |
1150 | - } |
1151 | - |
1152 | - ScopeProxy proxy = ScopeImpl::create(mw->create_scope_proxy(pair.first), mw->runtime(), pair.first); |
1153 | - mi->set_proxy(proxy); |
1154 | - auto meta = ScopeMetadataImpl::create(move(mi)); |
1155 | - |
1156 | - RegistryObject::ScopeExecData exec_data; |
1157 | - exec_data.scope_id = pair.first; |
1158 | - // get custom scope runner executable, if not set use default scoperunner |
1159 | - exec_data.scoperunner_path = scoperunner_path; |
1160 | - |
1161 | - if (click) |
1162 | - { |
1163 | - exec_data.confinement_profile = |
1164 | - scope_path.parent_path().filename().native(); |
1165 | - } |
1166 | - |
1167 | - exec_data.timeout_ms = timeout_ms; |
1168 | - |
1169 | - try |
1170 | - { |
1171 | - auto custom_exec = sc.scope_runner(); |
1172 | - if (custom_exec.empty()) |
1173 | - { |
1174 | - throw unity::InvalidArgumentException("Invalid scope runner executable for scope: " + pair.first); |
1175 | - } |
1176 | - exec_data.scoperunner_path = relative_scope_path_to_abs_path(custom_exec, scope_dir).native(); |
1177 | - } |
1178 | - catch (NotFoundException const&) |
1179 | - { |
1180 | - } |
1181 | - exec_data.runtime_config = config_file; |
1182 | - exec_data.scope_config = pair.second; |
1183 | - |
1184 | - registry->add_local_scope(pair.first, move(meta), exec_data); |
1185 | + add_local_scope(registry, pair, mw, scoperunner_path, config_file, click, timeout_ms); |
1186 | } |
1187 | catch (unity::Exception const& e) |
1188 | { |
1189 | @@ -403,7 +407,7 @@ |
1190 | |
1191 | // The registry object stores the local and remote scopes |
1192 | Executor::SPtr executor = make_shared<Executor>(); |
1193 | - RegistryObject::SPtr registry(new RegistryObject(*signal_handler_wrapper.death_observer, executor)); |
1194 | + RegistryObject::SPtr registry(new RegistryObject(*signal_handler_wrapper.death_observer, executor, middleware)); |
1195 | |
1196 | // Add the metadata for each scope to the lookup table. |
1197 | // We do this before starting any of the scopes, so aggregating scopes don't get a lookup failure if |
1198 | @@ -433,6 +437,38 @@ |
1199 | registry->set_remote_registry(middleware->ss_registry_proxy()); |
1200 | } |
1201 | |
1202 | + // Configure watches for scope install directories |
1203 | + ScopesWatcher local_scopes_watcher(registry, |
1204 | + [registry, &middleware, &scoperunner_path, &config_file, process_timeout] |
1205 | + (pair<string, string> const& scope) |
1206 | + { |
1207 | + try |
1208 | + { |
1209 | + add_local_scope(registry, scope, middleware, scoperunner_path, config_file, false, process_timeout); |
1210 | + } |
1211 | + catch (unity::Exception const& e) |
1212 | + { |
1213 | + error("ignoring installed scope \"" + scope.first + "\": cannot create metadata: " + e.what()); |
1214 | + } |
1215 | + }); |
1216 | + local_scopes_watcher.add_install_dir(scope_installdir); |
1217 | + local_scopes_watcher.add_install_dir(oem_installdir); |
1218 | + |
1219 | + ScopesWatcher click_scopes_watcher(registry, |
1220 | + [registry, &middleware, &scoperunner_path, &config_file, process_timeout] |
1221 | + (pair<string, string> const& scope) |
1222 | + { |
1223 | + try |
1224 | + { |
1225 | + add_local_scope(registry, scope, middleware, scoperunner_path, config_file, true, process_timeout); |
1226 | + } |
1227 | + catch (unity::Exception const& e) |
1228 | + { |
1229 | + error("ignoring installed scope \"" + scope.first + "\": cannot create metadata: " + e.what()); |
1230 | + } |
1231 | + }); |
1232 | + click_scopes_watcher.add_install_dir(click_installdir); |
1233 | + |
1234 | // Let's add the registry's state receiver to the middleware so that scopes can inform |
1235 | // the registry of state changes. |
1236 | middleware->add_state_receiver_object("StateReceiver", registry->state_receiver()); |
1237 | |
1238 | === modified file 'src/scopes/internal/MWRegistry.cpp' |
1239 | --- src/scopes/internal/MWRegistry.cpp 2014-01-23 11:28:34 +0000 |
1240 | +++ src/scopes/internal/MWRegistry.cpp 2014-05-27 12:46:42 +0000 |
1241 | @@ -16,7 +16,9 @@ |
1242 | * Authored by: Michi Henning <michi.henning@canonical.com> |
1243 | */ |
1244 | |
1245 | +#include <unity/scopes/internal/MiddlewareBase.h> |
1246 | #include <unity/scopes/internal/MWRegistry.h> |
1247 | +#include <unity/scopes/internal/RuntimeImpl.h> |
1248 | |
1249 | using namespace std; |
1250 | |
1251 | @@ -30,7 +32,8 @@ |
1252 | { |
1253 | |
1254 | MWRegistry::MWRegistry(MiddlewareBase* mw_base) : |
1255 | - MWObjectProxy(mw_base) |
1256 | + MWObjectProxy(mw_base), |
1257 | + mw_base_(mw_base) |
1258 | { |
1259 | } |
1260 | |
1261 | @@ -38,6 +41,26 @@ |
1262 | { |
1263 | } |
1264 | |
1265 | +void MWRegistry::set_list_update_callback(std::function<void()> callback) |
1266 | +{ |
1267 | + if (!subscriber_) |
1268 | + { |
1269 | + // Use lazy initialization here to only subscribe to the publisher if a callback is set |
1270 | + try |
1271 | + { |
1272 | + subscriber_ = mw_base_->create_subscriber(mw_base_->runtime()->registry_identity()); |
1273 | + } |
1274 | + catch (std::exception const& e) |
1275 | + { |
1276 | + cerr << "MWRegistry::set_list_update_callback(): failed to create registry subscriber: " << e.what() << endl; |
1277 | + } |
1278 | + } |
1279 | + if (subscriber_) |
1280 | + { |
1281 | + subscriber_->set_message_callback([callback](string const&){ callback(); }); |
1282 | + } |
1283 | +} |
1284 | + |
1285 | } // namespace internal |
1286 | |
1287 | } // namespace scopes |
1288 | |
1289 | === modified file 'src/scopes/internal/RegistryImpl.cpp' |
1290 | --- src/scopes/internal/RegistryImpl.cpp 2014-05-19 08:28:08 +0000 |
1291 | +++ src/scopes/internal/RegistryImpl.cpp 2014-05-27 12:46:42 +0000 |
1292 | @@ -71,6 +71,11 @@ |
1293 | return matching_entries; |
1294 | } |
1295 | |
1296 | +void RegistryImpl::set_list_update_callback(std::function<void()> callback) |
1297 | +{ |
1298 | + fwd()->set_list_update_callback(callback); |
1299 | +} |
1300 | + |
1301 | MWRegistryProxy RegistryImpl::fwd() |
1302 | { |
1303 | return dynamic_pointer_cast<MWRegistry>(proxy()); |
1304 | |
1305 | === modified file 'src/scopes/internal/RegistryObject.cpp' |
1306 | --- src/scopes/internal/RegistryObject.cpp 2014-05-23 07:43:20 +0000 |
1307 | +++ src/scopes/internal/RegistryObject.cpp 2014-05-27 12:46:42 +0000 |
1308 | @@ -19,6 +19,7 @@ |
1309 | #include <unity/scopes/internal/RegistryObject.h> |
1310 | |
1311 | #include <unity/scopes/internal/MWRegistry.h> |
1312 | +#include <unity/scopes/internal/RuntimeImpl.h> |
1313 | #include <unity/scopes/ScopeExceptions.h> |
1314 | #include <unity/UnityExceptions.h> |
1315 | |
1316 | @@ -38,7 +39,8 @@ |
1317 | namespace internal |
1318 | { |
1319 | |
1320 | -RegistryObject::RegistryObject(core::posix::ChildProcess::DeathObserver& death_observer, Executor::SPtr const& executor) |
1321 | +RegistryObject::RegistryObject(core::posix::ChildProcess::DeathObserver& death_observer, Executor::SPtr const& executor, |
1322 | + MiddlewareBase::SPtr middleware) |
1323 | : death_observer_(death_observer), |
1324 | death_observer_connection_ |
1325 | { |
1326 | @@ -58,6 +60,17 @@ |
1327 | }, |
1328 | executor_(executor) |
1329 | { |
1330 | + if (middleware) |
1331 | + { |
1332 | + try |
1333 | + { |
1334 | + publisher_ = middleware->create_publisher(middleware->runtime()->registry_identity()); |
1335 | + } |
1336 | + catch (std::exception const& e) |
1337 | + { |
1338 | + std::cerr << "RegistryObject(): failed to create registry publisher: " << e.what() << endl; |
1339 | + } |
1340 | + } |
1341 | } |
1342 | |
1343 | RegistryObject::~RegistryObject() |
1344 | @@ -209,6 +222,12 @@ |
1345 | } |
1346 | scopes_.insert(make_pair(scope_id, metadata)); |
1347 | scope_processes_.insert(make_pair(scope_id, ScopeProcess(exec_data))); |
1348 | + |
1349 | + if (publisher_) |
1350 | + { |
1351 | + // Send a blank message to subscribers to inform them that the registry has been updated |
1352 | + publisher_->send_message(""); |
1353 | + } |
1354 | return return_value; |
1355 | } |
1356 | |
1357 | @@ -224,7 +243,18 @@ |
1358 | lock_guard<decltype(mutex_)> lock(mutex_); |
1359 | |
1360 | scope_processes_.erase(scope_id); |
1361 | - return scopes_.erase(scope_id) == 1; |
1362 | + |
1363 | + if (scopes_.erase(scope_id) == 1) |
1364 | + { |
1365 | + if (publisher_) |
1366 | + { |
1367 | + // Send a blank message to subscribers to inform them that the registry has been updated |
1368 | + publisher_->send_message(""); |
1369 | + } |
1370 | + return true; |
1371 | + } |
1372 | + |
1373 | + return false; |
1374 | } |
1375 | |
1376 | void RegistryObject::set_remote_registry(MWRegistryProxy const& remote_registry) |
1377 | |
1378 | === modified file 'src/scopes/internal/zmq_middleware/ZmqSubscriber.cpp' |
1379 | --- src/scopes/internal/zmq_middleware/ZmqSubscriber.cpp 2014-05-19 09:17:14 +0000 |
1380 | +++ src/scopes/internal/zmq_middleware/ZmqSubscriber.cpp 2014-05-27 12:46:42 +0000 |
1381 | @@ -67,9 +67,9 @@ |
1382 | thread_ = std::thread(&ZmqSubscriber::subscriber_thread, this); |
1383 | |
1384 | std::unique_lock<std::mutex> lock(mutex_); |
1385 | - cond_.wait(lock, [this] { return thread_state_ == Running || thread_state_ == Failed; }); |
1386 | + cond_.wait(lock, [this] { return thread_state_ == Running || thread_state_ == Stopped; }); |
1387 | |
1388 | - if (thread_state_ == Failed) |
1389 | + if (thread_state_ == Stopped) |
1390 | { |
1391 | if (thread_.joinable()) |
1392 | { |
1393 | @@ -89,7 +89,11 @@ |
1394 | |
1395 | ZmqSubscriber::~ZmqSubscriber() |
1396 | { |
1397 | - thread_stopper_->stop(); |
1398 | + { |
1399 | + std::lock_guard<std::mutex> lock(mutex_); |
1400 | + callback_ = nullptr; |
1401 | + thread_stopper_ = nullptr; |
1402 | + } |
1403 | |
1404 | if (thread_.joinable()) |
1405 | { |
1406 | @@ -137,6 +141,7 @@ |
1407 | std::string message; |
1408 | while (true) |
1409 | { |
1410 | + // poll() throws when the zmq context is destroyed (hense stopping the thread) |
1411 | poller.poll(); |
1412 | |
1413 | // Flush out the message queue before stopping the thread |
1414 | @@ -170,8 +175,10 @@ |
1415 | catch (...) |
1416 | { |
1417 | std::lock_guard<std::mutex> lock(mutex_); |
1418 | + callback_ = nullptr; |
1419 | + thread_stopper_ = nullptr; |
1420 | thread_exception_ = std::current_exception(); |
1421 | - thread_state_ = Failed; |
1422 | + thread_state_ = Stopped; |
1423 | cond_.notify_all(); |
1424 | } |
1425 | } |
1426 | |
1427 | === modified file 'test/gtest/scopes/Registry/CMakeLists.txt' |
1428 | --- test/gtest/scopes/Registry/CMakeLists.txt 2014-04-03 16:50:44 +0000 |
1429 | +++ test/gtest/scopes/Registry/CMakeLists.txt 2014-05-27 12:46:42 +0000 |
1430 | @@ -13,3 +13,4 @@ |
1431 | |
1432 | add_test(Registry Registry_test) |
1433 | add_subdirectory(scopes) |
1434 | +add_subdirectory(other_scopes) |
1435 | |
1436 | === modified file 'test/gtest/scopes/Registry/Registry_test.cpp' |
1437 | --- test/gtest/scopes/Registry/Registry_test.cpp 2014-05-23 00:58:11 +0000 |
1438 | +++ test/gtest/scopes/Registry/Registry_test.cpp 2014-05-27 12:46:42 +0000 |
1439 | @@ -23,14 +23,15 @@ |
1440 | #include <unity/scopes/CategorisedResult.h> |
1441 | #include <gtest/gtest.h> |
1442 | |
1443 | +#include <boost/filesystem/operations.hpp> |
1444 | #include <condition_variable> |
1445 | #include <functional> |
1446 | #include <mutex> |
1447 | +#include <signal.h> |
1448 | #include <thread> |
1449 | - |
1450 | -#include <signal.h> |
1451 | #include <unistd.h> |
1452 | |
1453 | +using namespace boost; |
1454 | using namespace unity::scopes; |
1455 | |
1456 | class Receiver : public SearchListenerBase |
1457 | @@ -114,6 +115,141 @@ |
1458 | EXPECT_TRUE(receiver->wait_until_finished()); |
1459 | } |
1460 | |
1461 | +TEST(Registry, update_notify) |
1462 | +{ |
1463 | + bool update_received_ = false; |
1464 | + std::mutex mutex_; |
1465 | + std::condition_variable cond_; |
1466 | + |
1467 | + Runtime::UPtr rt = Runtime::create(TEST_RUNTIME_FILE); |
1468 | + RegistryProxy r = rt->registry(); |
1469 | + |
1470 | + // Configure registry update callback |
1471 | + r->set_list_update_callback([&update_received_, &mutex_, &cond_] |
1472 | + { |
1473 | + std::lock_guard<std::mutex> lock(mutex_); |
1474 | + update_received_ = true; |
1475 | + cond_.notify_one(); |
1476 | + }); |
1477 | + auto wait_for_update = [&update_received_, &mutex_, &cond_] |
1478 | + { |
1479 | + // Flush out update notifications |
1480 | + std::unique_lock<std::mutex> lock(mutex_); |
1481 | + while (cond_.wait_for(lock, std::chrono::milliseconds(500), [&update_received_] { return update_received_; })) |
1482 | + { |
1483 | + update_received_ = false; |
1484 | + } |
1485 | + update_received_ = false; |
1486 | + }; |
1487 | + |
1488 | + system::error_code ec; |
1489 | + |
1490 | + // First check that we have 2 scopes registered |
1491 | + MetadataMap list = r->list(); |
1492 | + EXPECT_EQ(2, list.size()); |
1493 | + EXPECT_NE(list.end(), list.find("testscopeA")); |
1494 | + EXPECT_NE(list.end(), list.find("testscopeB")); |
1495 | + EXPECT_EQ(list.end(), list.find("testscopeC")); |
1496 | + EXPECT_EQ(list.end(), list.find("testscopeD")); |
1497 | + |
1498 | + // Move testscopeC into the scopes folder |
1499 | + std::cout << "Move testscopeC into the scopes folder" << std::endl; |
1500 | + filesystem::rename(TEST_RUNTIME_PATH "/other_scopes/testscopeC", TEST_RUNTIME_PATH "/scopes/testscopeC", ec); |
1501 | + ASSERT_EQ("Success", ec.message()); |
1502 | + wait_for_update(); |
1503 | + |
1504 | + // Now check that we have 3 scopes registered |
1505 | + list = r->list(); |
1506 | + EXPECT_EQ(3, list.size()); |
1507 | + EXPECT_NE(list.end(), list.find("testscopeA")); |
1508 | + EXPECT_NE(list.end(), list.find("testscopeB")); |
1509 | + EXPECT_NE(list.end(), list.find("testscopeC")); |
1510 | + EXPECT_EQ(list.end(), list.find("testscopeD")); |
1511 | + |
1512 | + // Make a symlink to testscopeD in the scopes folder |
1513 | + std::cout << "Make a symlink to testscopeD in the scopes folder" << std::endl; |
1514 | + filesystem::create_symlink(TEST_RUNTIME_PATH "/other_scopes/testscopeD", TEST_RUNTIME_PATH "/scopes/testscopeD", ec); |
1515 | + ASSERT_EQ("Success", ec.message()); |
1516 | + wait_for_update(); |
1517 | + |
1518 | + // Now check that we have 4 scopes registered |
1519 | + list = r->list(); |
1520 | + EXPECT_EQ(4, list.size()); |
1521 | + EXPECT_NE(list.end(), list.find("testscopeA")); |
1522 | + EXPECT_NE(list.end(), list.find("testscopeB")); |
1523 | + EXPECT_NE(list.end(), list.find("testscopeC")); |
1524 | + EXPECT_NE(list.end(), list.find("testscopeD")); |
1525 | + |
1526 | + // Move testscopeC back into the other_scopes folder |
1527 | + std::cout << "Move testscopeC back into the other_scopes folder" << std::endl; |
1528 | + filesystem::rename(TEST_RUNTIME_PATH "/scopes/testscopeC", TEST_RUNTIME_PATH "/other_scopes/testscopeC", ec); |
1529 | + ASSERT_EQ("Success", ec.message()); |
1530 | + wait_for_update(); |
1531 | + |
1532 | + // Now check that we have 3 scopes registered again |
1533 | + list = r->list(); |
1534 | + EXPECT_EQ(3, list.size()); |
1535 | + EXPECT_NE(list.end(), list.find("testscopeA")); |
1536 | + EXPECT_NE(list.end(), list.find("testscopeB")); |
1537 | + EXPECT_EQ(list.end(), list.find("testscopeC")); |
1538 | + EXPECT_NE(list.end(), list.find("testscopeD")); |
1539 | + |
1540 | + // Remove symlink to testscopeD from the scopes folder |
1541 | + std::cout << "Remove symlink to testscopeD from the scopes folder" << std::endl; |
1542 | + filesystem::remove(TEST_RUNTIME_PATH "/scopes/testscopeD", ec); |
1543 | + ASSERT_EQ("Success", ec.message()); |
1544 | + wait_for_update(); |
1545 | + |
1546 | + // Now check that we are back to having 2 scopes registered |
1547 | + list = r->list(); |
1548 | + EXPECT_EQ(2, list.size()); |
1549 | + EXPECT_NE(list.end(), list.find("testscopeA")); |
1550 | + EXPECT_NE(list.end(), list.find("testscopeB")); |
1551 | + EXPECT_EQ(list.end(), list.find("testscopeC")); |
1552 | + EXPECT_EQ(list.end(), list.find("testscopeD")); |
1553 | + |
1554 | + // Make a folder in scopes named "testfolder" |
1555 | + std::cout << "Make a folder in scopes named \"testfolder\"" << std::endl; |
1556 | + filesystem::create_directory(TEST_RUNTIME_PATH "/scopes/testfolder", ec); |
1557 | + ASSERT_EQ("Success", ec.message()); |
1558 | + |
1559 | + // Check that no scopes were registered |
1560 | + list = r->list(); |
1561 | + EXPECT_EQ(2, list.size()); |
1562 | + EXPECT_NE(list.end(), list.find("testscopeA")); |
1563 | + EXPECT_NE(list.end(), list.find("testscopeB")); |
1564 | + EXPECT_EQ(list.end(), list.find("testscopeC")); |
1565 | + EXPECT_EQ(list.end(), list.find("testscopeD")); |
1566 | + |
1567 | + // Make a symlink to testscopeC.ini in testfolder |
1568 | + std::cout << "Make a symlink to testscopeC.ini in testfolder" << std::endl; |
1569 | + filesystem::create_symlink(TEST_RUNTIME_PATH "/other_scopes/testscopeC/testscopeC.ini", TEST_RUNTIME_PATH "/scopes/testfolder/testscopeC.ini", ec); |
1570 | + ASSERT_EQ("Success", ec.message()); |
1571 | + wait_for_update(); |
1572 | + |
1573 | + // Now check that we have 3 scopes registered |
1574 | + list = r->list(); |
1575 | + EXPECT_EQ(3, list.size()); |
1576 | + EXPECT_NE(list.end(), list.find("testscopeA")); |
1577 | + EXPECT_NE(list.end(), list.find("testscopeB")); |
1578 | + EXPECT_NE(list.end(), list.find("testscopeC")); |
1579 | + EXPECT_EQ(list.end(), list.find("testscopeD")); |
1580 | + |
1581 | + // Remove testfolder |
1582 | + std::cout << "Remove testfolder" << std::endl; |
1583 | + filesystem::remove_all(TEST_RUNTIME_PATH "/scopes/testfolder", ec); |
1584 | + ASSERT_EQ("Success", ec.message()); |
1585 | + wait_for_update(); |
1586 | + |
1587 | + // Now check that we are back to having 2 scopes registered |
1588 | + list = r->list(); |
1589 | + EXPECT_EQ(2, list.size()); |
1590 | + EXPECT_NE(list.end(), list.find("testscopeA")); |
1591 | + EXPECT_NE(list.end(), list.find("testscopeB")); |
1592 | + EXPECT_EQ(list.end(), list.find("testscopeC")); |
1593 | + EXPECT_EQ(list.end(), list.find("testscopeD")); |
1594 | +} |
1595 | + |
1596 | int main(int argc, char **argv) |
1597 | { |
1598 | ::testing::InitGoogleTest(&argc, argv); |
1599 | |
1600 | === added directory 'test/gtest/scopes/Registry/other_scopes' |
1601 | === added file 'test/gtest/scopes/Registry/other_scopes/CMakeLists.txt' |
1602 | --- test/gtest/scopes/Registry/other_scopes/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
1603 | +++ test/gtest/scopes/Registry/other_scopes/CMakeLists.txt 2014-05-27 12:46:42 +0000 |
1604 | @@ -0,0 +1,2 @@ |
1605 | +add_subdirectory(testscopeC) |
1606 | +add_subdirectory(testscopeD) |
1607 | |
1608 | === added directory 'test/gtest/scopes/Registry/other_scopes/testscopeC' |
1609 | === added file 'test/gtest/scopes/Registry/other_scopes/testscopeC/CMakeLists.txt' |
1610 | --- test/gtest/scopes/Registry/other_scopes/testscopeC/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
1611 | +++ test/gtest/scopes/Registry/other_scopes/testscopeC/CMakeLists.txt 2014-05-27 12:46:42 +0000 |
1612 | @@ -0,0 +1,1 @@ |
1613 | +configure_file(testscopeC.ini.in testscopeC.ini) |
1614 | |
1615 | === added file 'test/gtest/scopes/Registry/other_scopes/testscopeC/testscopeC.ini.in' |
1616 | --- test/gtest/scopes/Registry/other_scopes/testscopeC/testscopeC.ini.in 1970-01-01 00:00:00 +0000 |
1617 | +++ test/gtest/scopes/Registry/other_scopes/testscopeC/testscopeC.ini.in 2014-05-27 12:46:42 +0000 |
1618 | @@ -0,0 +1,8 @@ |
1619 | +[ScopeConfig] |
1620 | +DisplayName = scope-C.DisplayName |
1621 | +Description = scope-C.Description |
1622 | +Art = /foo/scope-C.Art |
1623 | +Author = Canonical Ltd. |
1624 | +Icon = /foo/scope-C.Icon |
1625 | +SearchHint = scope-C.SearchHint |
1626 | +HotKey = scope-C.HotKey |
1627 | |
1628 | === added directory 'test/gtest/scopes/Registry/other_scopes/testscopeD' |
1629 | === added file 'test/gtest/scopes/Registry/other_scopes/testscopeD/CMakeLists.txt' |
1630 | --- test/gtest/scopes/Registry/other_scopes/testscopeD/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
1631 | +++ test/gtest/scopes/Registry/other_scopes/testscopeD/CMakeLists.txt 2014-05-27 12:46:42 +0000 |
1632 | @@ -0,0 +1,1 @@ |
1633 | +configure_file(testscopeD.ini.in testscopeD.ini) |
1634 | |
1635 | === added file 'test/gtest/scopes/Registry/other_scopes/testscopeD/testscopeD.ini.in' |
1636 | --- test/gtest/scopes/Registry/other_scopes/testscopeD/testscopeD.ini.in 1970-01-01 00:00:00 +0000 |
1637 | +++ test/gtest/scopes/Registry/other_scopes/testscopeD/testscopeD.ini.in 2014-05-27 12:46:42 +0000 |
1638 | @@ -0,0 +1,8 @@ |
1639 | +[ScopeConfig] |
1640 | +DisplayName = scope-D.DisplayName |
1641 | +Description = scope-D.Description |
1642 | +Art = /foo/scope-D.Art |
1643 | +Author = Canonical Ltd. |
1644 | +Icon = /foo/scope-D.Icon |
1645 | +SearchHint = scope-D.SearchHint |
1646 | +HotKey = scope-D.HotKey |
1647 | |
1648 | === modified file 'test/gtest/scopes/internal/RegistryObject/RegistryObject_test.cpp' |
1649 | --- test/gtest/scopes/internal/RegistryObject/RegistryObject_test.cpp 2014-05-22 08:45:05 +0000 |
1650 | +++ test/gtest/scopes/internal/RegistryObject/RegistryObject_test.cpp 2014-05-27 12:46:42 +0000 |
1651 | @@ -164,7 +164,7 @@ |
1652 | exec_data.confinement_profile = confinement_profile; |
1653 | exec_data.timeout_ms = 1500; |
1654 | |
1655 | - registry.reset(new RegistryObject(*death_observer(), executor)); |
1656 | + registry.reset(new RegistryObject(*death_observer(), executor, nullptr)); |
1657 | registry->add_local_scope("scope-id", meta, exec_data); |
1658 | registry->locate("scope-id"); |
1659 | EXPECT_TRUE(registry->is_scope_running("scope-id")); |
1660 | |
1661 | === modified file 'test/gtest/scopes/internal/zmq_middleware/RegistryI/RegistryI_test.cpp' |
1662 | --- test/gtest/scopes/internal/zmq_middleware/RegistryI/RegistryI_test.cpp 2014-05-22 23:21:46 +0000 |
1663 | +++ test/gtest/scopes/internal/zmq_middleware/RegistryI/RegistryI_test.cpp 2014-05-27 12:46:42 +0000 |
1664 | @@ -100,7 +100,7 @@ |
1665 | |
1666 | MiddlewareBase::SPtr middleware = runtime->factory()->create(identity, mw_kind, mw_configfile); |
1667 | Executor::SPtr executor = make_shared<Executor>(); |
1668 | - RegistryObject::SPtr ro(make_shared<RegistryObject>(*scope.death_observer, executor)); |
1669 | + RegistryObject::SPtr ro(make_shared<RegistryObject>(*scope.death_observer, executor, middleware)); |
1670 | auto registry = middleware->add_registry_object(identity, ro); |
1671 | auto p = middleware->create_scope_proxy("scope1", "ipc:///tmp/scope1"); |
1672 | EXPECT_TRUE(ro->add_local_scope("scope1", move(make_meta("scope1", p, middleware)), |
1673 | @@ -123,7 +123,7 @@ |
1674 | |
1675 | MiddlewareBase::SPtr middleware = runtime->factory()->create(identity, mw_kind, mw_configfile); |
1676 | Executor::SPtr executor = make_shared<Executor>(); |
1677 | - RegistryObject::SPtr ro(make_shared<RegistryObject>(*scope.death_observer, executor)); |
1678 | + RegistryObject::SPtr ro(make_shared<RegistryObject>(*scope.death_observer, executor, middleware)); |
1679 | auto registry = middleware->add_registry_object(identity, ro); |
1680 | |
1681 | auto r = runtime->registry(); |
1682 | @@ -172,7 +172,7 @@ |
1683 | |
1684 | MiddlewareBase::SPtr middleware = runtime->factory()->create(identity, mw_kind, mw_configfile); |
1685 | Executor::SPtr executor = make_shared<Executor>(); |
1686 | - RegistryObject::SPtr ro(make_shared<RegistryObject>(*scope.death_observer, executor)); |
1687 | + RegistryObject::SPtr ro(make_shared<RegistryObject>(*scope.death_observer, executor, middleware)); |
1688 | auto registry = middleware->add_registry_object(identity, ro); |
1689 | |
1690 | auto r = runtime->registry(); |
1691 | @@ -224,7 +224,7 @@ |
1692 | |
1693 | MiddlewareBase::SPtr middleware = runtime->factory()->create(identity, mw_kind, mw_configfile); |
1694 | Executor::SPtr executor = make_shared<Executor>(); |
1695 | - RegistryObject::SPtr ro(make_shared<RegistryObject>(*scope.death_observer, executor)); |
1696 | + RegistryObject::SPtr ro(make_shared<RegistryObject>(*scope.death_observer, executor, middleware)); |
1697 | RegistryObject::ScopeExecData dummy_exec_data; |
1698 | auto registry = middleware->add_registry_object(identity, ro); |
1699 | auto proxy = middleware->create_scope_proxy("scope1", "ipc:///tmp/scope1"); |
1700 | @@ -286,7 +286,7 @@ |
1701 | { |
1702 | public: |
1703 | MockRegistryObject(core::posix::ChildProcess::DeathObserver& death_observer) |
1704 | - : RegistryObject(death_observer, make_shared<Executor>()) |
1705 | + : RegistryObject(death_observer, make_shared<Executor>(), nullptr) |
1706 | { |
1707 | } |
1708 | |
1709 | @@ -393,7 +393,7 @@ |
1710 | mw = rt->factory()->find(reg_id, mw_kind); |
1711 | |
1712 | Executor::SPtr executor = make_shared<Executor>(); |
1713 | - reg = RegistryObject::SPtr(new RegistryObject(*scope.death_observer, executor)); |
1714 | + reg = RegistryObject::SPtr(new RegistryObject(*scope.death_observer, executor, mw)); |
1715 | mw->add_registry_object(reg_id, reg); |
1716 | mw->add_state_receiver_object("StateReceiver", reg->state_receiver()); |
1717 |
I've made changes to the public Registry class so I assume the API version needs to be bumped up. Could someone please give me a run down what exactly I need to do. I've never changed the public API before. Thanks!