summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2009-06-19 00:17:30 +0000
committerNat Goodspeed <nat@lindenlab.com>2009-06-19 00:17:30 +0000
commitdc3833f31b8a20220ddb1775e1625c016c397435 (patch)
tree166ce57be2578ea03d1e20976977e4ca6c1f1dba
parent8a8edfd98c7fced23f4c1f8dd5e4f65e1cadfce8 (diff)
DEV-31980: extract dispatch-by-string-name logic from LLAresListener to new
LLEventDispatcher and LLDispatchListener classes. See LLAresListener for example usage.
-rw-r--r--indra/llcommon/CMakeLists.txt2
-rw-r--r--indra/llcommon/lleventdispatcher.cpp123
-rw-r--r--indra/llcommon/lleventdispatcher.h108
-rw-r--r--indra/llmessage/llareslistener.cpp46
-rw-r--r--indra/llmessage/llareslistener.h14
-rw-r--r--indra/llmessage/tests/llareslistener_test.cpp2
6 files changed, 242 insertions, 53 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index e920475f54..88acb70b8a 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -37,6 +37,7 @@ set(llcommon_SOURCE_FILES
llerrorthread.cpp
llevent.cpp
lleventcoro.cpp
+ lleventdispatcher.cpp
lleventfilter.cpp
llevents.cpp
llfasttimer.cpp
@@ -126,6 +127,7 @@ set(llcommon_HEADER_FILES
llerrorthread.h
llevent.h
lleventcoro.h
+ lleventdispatcher.h
lleventfilter.h
llevents.h
lleventemitter.h
diff --git a/indra/llcommon/lleventdispatcher.cpp b/indra/llcommon/lleventdispatcher.cpp
new file mode 100644
index 0000000000..2dbd59b156
--- /dev/null
+++ b/indra/llcommon/lleventdispatcher.cpp
@@ -0,0 +1,123 @@
+/**
+ * @file lleventdispatcher.cpp
+ * @author Nat Goodspeed
+ * @date 2009-06-18
+ * @brief Implementation for lleventdispatcher.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if LL_WINDOWS
+#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
+#endif
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "lleventdispatcher.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+#include "llevents.h"
+#include "llerror.h"
+#include "llsdutil.h"
+
+LLEventDispatcher::LLEventDispatcher(const std::string& desc, const std::string& key):
+ mDesc(desc),
+ mKey(key)
+{
+}
+
+LLEventDispatcher::~LLEventDispatcher()
+{
+}
+
+/// Register a callable by name
+void LLEventDispatcher::add(const std::string& name, const Callable& callable, const LLSD& required)
+{
+ mDispatch[name] = DispatchMap::mapped_type(callable, required);
+}
+
+void LLEventDispatcher::addFail(const std::string& name, const std::string& classname) const
+{
+ LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << ")::add(" << name
+ << "): " << classname << " is not a subclass "
+ << "of LLEventDispatcher" << LL_ENDL;
+}
+
+/// Unregister a callable
+bool LLEventDispatcher::remove(const std::string& name)
+{
+ DispatchMap::iterator found = mDispatch.find(name);
+ if (found == mDispatch.end())
+ {
+ return false;
+ }
+ mDispatch.erase(found);
+ return true;
+}
+
+/// Call a registered callable with an explicitly-specified name. If no
+/// such callable exists, die with LL_ERRS.
+void LLEventDispatcher::operator()(const std::string& name, const LLSD& event) const
+{
+ if (! attemptCall(name, event))
+ {
+ LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): '" << name
+ << "' not found" << LL_ENDL;
+ }
+}
+
+/// Extract the @a key value from the incoming @a event, and call the
+/// callable whose name is specified by that map @a key. If no such
+/// callable exists, die with LL_ERRS.
+void LLEventDispatcher::operator()(const LLSD& event) const
+{
+ // This could/should be implemented in terms of the two-arg overload.
+ // However -- we can produce a more informative error message.
+ std::string name(event[mKey]);
+ if (! attemptCall(name, event))
+ {
+ LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): bad " << mKey
+ << " value '" << name << "'" << LL_ENDL;
+ }
+}
+
+bool LLEventDispatcher::attemptCall(const std::string& name, const LLSD& event) const
+{
+ DispatchMap::const_iterator found = mDispatch.find(name);
+ if (found == mDispatch.end())
+ {
+ // The reason we only return false, leaving it up to our caller to die
+ // with LL_ERRS, is that different callers have different amounts of
+ // available information.
+ return false;
+ }
+ // Found the name, so it's plausible to even attempt the call. But first,
+ // validate the syntax of the event itself.
+ std::string mismatch(llsd_matches(found->second.second, event));
+ if (! mismatch.empty())
+ {
+ LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << ") calling '" << name
+ << "': bad request: " << mismatch << LL_ENDL;
+ }
+ // Event syntax looks good, go for it!
+ (found->second.first)(event);
+ return true; // tell caller we were able to call
+}
+
+LLDispatchListener::LLDispatchListener(const std::string& pumpname, const std::string& key):
+ LLEventDispatcher(pumpname, key),
+ mPump(pumpname, true), // allow tweaking for uniqueness
+ mBoundListener(mPump.listen("self", boost::bind(&LLDispatchListener::process, this, _1)))
+{
+}
+
+bool LLDispatchListener::process(const LLSD& event)
+{
+ (*this)(event);
+ return false;
+}
diff --git a/indra/llcommon/lleventdispatcher.h b/indra/llcommon/lleventdispatcher.h
new file mode 100644
index 0000000000..d75055fd6f
--- /dev/null
+++ b/indra/llcommon/lleventdispatcher.h
@@ -0,0 +1,108 @@
+/**
+ * @file lleventdispatcher.h
+ * @author Nat Goodspeed
+ * @date 2009-06-18
+ * @brief Central mechanism for dispatching events by string name. This is
+ * useful when you have a single LLEventPump listener on which you can
+ * request different operations, vs. instantiating a different
+ * LLEventPump for each such operation.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLEVENTDISPATCHER_H)
+#define LL_LLEVENTDISPATCHER_H
+
+#include <string>
+#include <map>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <typeinfo>
+#include "llevents.h"
+
+class LLSD;
+
+/**
+ * Given an LLSD map, examine a string-valued key and call a corresponding
+ * callable. This class is designed to be contained by an LLEventPump
+ * listener class that will register some of its own methods, though any
+ * callable can be used.
+ */
+class LLEventDispatcher
+{
+public:
+ LLEventDispatcher(const std::string& desc, const std::string& key);
+ virtual ~LLEventDispatcher();
+
+ /// Accept any C++ callable, typically a boost::bind() expression
+ typedef boost::function<void(const LLSD&)> Callable;
+
+ /// Register a @a callable by @a name. The optional @a required parameter
+ /// is used to validate the structure of each incoming event (see
+ /// llsd_matches()).
+ void add(const std::string& name, const Callable& callable, const LLSD& required=LLSD());
+
+ /// Special case: a subclass of this class can register a @a method
+ /// without explicitly specifying the <tt>boost::bind()</tt> expression.
+ /// The optional @a required parameter is used to validate the structure
+ /// of each incoming event (see llsd_matches()).
+ template <class CLASS>
+ void add(const std::string& name, void (CLASS::*method)(const LLSD&),
+ const LLSD& required=LLSD())
+ {
+ CLASS* downcast = dynamic_cast<CLASS*>(this);
+ if (! downcast)
+ {
+ addFail(name, typeid(CLASS).name());
+ }
+ else
+ {
+ add(name, boost::bind(method, downcast, _1), required);
+ }
+ }
+
+ /// Unregister a callable
+ bool remove(const std::string& name);
+
+ /// Call a registered callable with an explicitly-specified name. If no
+ /// such callable exists, die with LL_ERRS. If the @a event fails to match
+ /// the @a required prototype specified at add() time, die with LL_ERRS.
+ void operator()(const std::string& name, const LLSD& event) const;
+
+ /// Extract the @a key value from the incoming @a event, and call the
+ /// callable whose name is specified by that map @a key. If no such
+ /// callable exists, die with LL_ERRS. If the @a event fails to match the
+ /// @a required prototype specified at add() time, die with LL_ERRS.
+ void operator()(const LLSD& event) const;
+
+private:
+ void addFail(const std::string& name, const std::string& classname) const;
+ /// try to dispatch, return @c true if success
+ bool attemptCall(const std::string& name, const LLSD& event) const;
+
+ std::string mDesc, mKey;
+ typedef std::map<std::string, std::pair<Callable, LLSD> > DispatchMap;
+ DispatchMap mDispatch;
+};
+
+/**
+ * Bundle an LLEventPump and a listener with an LLEventDispatcher. A class
+ * that contains (or derives from) LLDispatchListener need only specify the
+ * LLEventPump name and dispatch key, and add() its methods. Incoming events
+ * will automatically be dispatched.
+ */
+class LLDispatchListener: public LLEventDispatcher
+{
+public:
+ LLDispatchListener(const std::string& pumpname, const std::string& key);
+
+private:
+ bool process(const LLSD& event);
+
+ LLEventStream mPump;
+ LLTempBoundListener mBoundListener;
+};
+
+#endif /* ! defined(LL_LLEVENTDISPATCHER_H) */
diff --git a/indra/llmessage/llareslistener.cpp b/indra/llmessage/llareslistener.cpp
index 4bf375069d..a8beb8cbde 100644
--- a/indra/llmessage/llareslistener.cpp
+++ b/indra/llmessage/llareslistener.cpp
@@ -9,10 +9,6 @@
* $/LicenseInfo$
*/
-#if LL_WINDOWS
-#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
-#endif
-
// Precompiled header
#include "linden_common.h"
// associated header
@@ -27,35 +23,13 @@
#include "llsdutil.h"
LLAresListener::LLAresListener(const std::string& pumpname, LLAres* llares):
- mAres(llares),
- mBoundListener(LLEventPumps::instance().
- obtain(pumpname).
- listen("LLAresListener", boost::bind(&LLAresListener::process, this, _1)))
-{
- // Insert an entry into our mDispatch map for every method we want to be
- // able to invoke via this event API.
- mDispatch["rewriteURI"] = boost::bind(&LLAresListener::rewriteURI, this, _1);
-}
-
-bool LLAresListener::process(const LLSD& command)
+ LLDispatchListener(pumpname, "op"),
+ mAres(llares)
{
- const std::string op(command["op"]);
- // Look up the requested operation.
- DispatchMap::const_iterator found = mDispatch.find(op);
- if (found == mDispatch.end())
- {
- // There's no feedback other than our own reply. If somebody asks
- // for an operation that's not supported (perhaps because of a
- // typo?), unless we holler loudly, the request will be silently
- // ignored. Throwing a tantrum on such errors will hopefully make
- // this product more robust.
- LL_ERRS("LLAresListener") << "Unsupported request " << op << LL_ENDL;
- return false;
- }
- // Having found the operation, call it.
- found->second(command);
- // Conventional LLEventPump listener return
- return false;
+ // add() every method we want to be able to invoke via this event API.
+ // Optional third parameter validates expected LLSD request structure.
+ add("rewriteURI", &LLAresListener::rewriteURI,
+ LLSD().insert("uri", LLSD()).insert("reply", LLSD()));
}
/// This UriRewriteResponder subclass packages returned URIs as an LLSD
@@ -97,13 +71,5 @@ private:
void LLAresListener::rewriteURI(const LLSD& data)
{
- static LLSD required(LLSD().insert("uri", LLSD()).insert("reply", LLSD()));
- // Validate that the request is well-formed
- std::string mismatch(llsd_matches(required, data));
- if (! mismatch.empty())
- {
- LL_ERRS("LLAresListener") << "bad rewriteURI request: " << mismatch << LL_ENDL;
- }
- // Looks as though we have what we need; issue the request
mAres->rewriteURI(data["uri"], new UriRewriteResponder(data));
}
diff --git a/indra/llmessage/llareslistener.h b/indra/llmessage/llareslistener.h
index 8835440c5d..bf093b3d3d 100644
--- a/indra/llmessage/llareslistener.h
+++ b/indra/llmessage/llareslistener.h
@@ -14,33 +14,23 @@
#if ! defined(LL_LLARESLISTENER_H)
#define LL_LLARESLISTENER_H
-#include <string>
-#include <map>
-#include <boost/function.hpp>
-#include "llevents.h"
+#include "lleventdispatcher.h"
class LLAres;
class LLSD;
/// Listen on an LLEventPump with specified name for LLAres request events.
-class LLAresListener
+class LLAresListener: public LLDispatchListener
{
public:
/// Specify the pump name on which to listen, and bind the LLAres instance
/// to use (e.g. gAres)
LLAresListener(const std::string& pumpname, LLAres* llares);
- /// Handle request events on the event pump specified at construction time
- bool process(const LLSD& command);
-
private:
/// command["op"] == "rewriteURI"
void rewriteURI(const LLSD& data);
- typedef boost::function<void(const LLSD&)> Callable;
- typedef std::map<std::string, Callable> DispatchMap;
- DispatchMap mDispatch;
- LLTempBoundListener mBoundListener;
LLAres* mAres;
};
diff --git a/indra/llmessage/tests/llareslistener_test.cpp b/indra/llmessage/tests/llareslistener_test.cpp
index 215a3806f8..ac4886ccf4 100644
--- a/indra/llmessage/tests/llareslistener_test.cpp
+++ b/indra/llmessage/tests/llareslistener_test.cpp
@@ -130,7 +130,7 @@ namespace tut
{
threw = e.what();
}
- ensure_contains("LLAresListener bad op", threw, "Unsupported");
+ ensure_contains("LLAresListener bad op", threw, "bad");
}
template<> template<>