diff options
Diffstat (limited to 'indra')
| -rw-r--r-- | indra/llcommon/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | indra/llcommon/lleventdispatcher.cpp | 123 | ||||
| -rw-r--r-- | indra/llcommon/lleventdispatcher.h | 108 | ||||
| -rw-r--r-- | indra/llmessage/llareslistener.cpp | 46 | ||||
| -rw-r--r-- | indra/llmessage/llareslistener.h | 14 | ||||
| -rw-r--r-- | indra/llmessage/tests/llareslistener_test.cpp | 2 | 
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<> | 
