OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright 2011 The WebRTC Project Authors. All rights reserved. |
| 3 * |
| 4 * Use of this source code is governed by a BSD-style license |
| 5 * that can be found in the LICENSE file in the root of the source |
| 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ |
| 10 |
| 11 #ifndef WEBRTC_LIBJINGLE_XMPP_PUBSUBSTATECLIENT_H_ |
| 12 #define WEBRTC_LIBJINGLE_XMPP_PUBSUBSTATECLIENT_H_ |
| 13 |
| 14 #include <map> |
| 15 #include <memory> |
| 16 #include <string> |
| 17 #include <vector> |
| 18 |
| 19 #include "third_party/libjingle_xmpp/xmllite/qname.h" |
| 20 #include "third_party/libjingle_xmpp/xmllite/xmlelement.h" |
| 21 #include "third_party/libjingle_xmpp/xmpp/constants.h" |
| 22 #include "third_party/libjingle_xmpp/xmpp/jid.h" |
| 23 #include "third_party/libjingle_xmpp/xmpp/pubsubclient.h" |
| 24 #include "webrtc/base/constructormagic.h" |
| 25 #include "webrtc/base/sigslot.h" |
| 26 #include "webrtc/base/sigslotrepeater.h" |
| 27 |
| 28 namespace buzz { |
| 29 |
| 30 // To handle retracts correctly, we need to remember certain details |
| 31 // about an item. We could just cache the entire XML element, but |
| 32 // that would take more memory and require re-parsing. |
| 33 struct StateItemInfo { |
| 34 std::string published_nick; |
| 35 std::string publisher_nick; |
| 36 }; |
| 37 |
| 38 // Represents a PubSub state change. Usually, the key is the nick, |
| 39 // but not always. It's a per-state-type thing. Look below on how keys are |
| 40 // computed. |
| 41 template <typename C> |
| 42 struct PubSubStateChange { |
| 43 // The nick of the user changing the state. |
| 44 std::string publisher_nick; |
| 45 // The nick of the user whose state is changing. |
| 46 std::string published_nick; |
| 47 C old_state; |
| 48 C new_state; |
| 49 }; |
| 50 |
| 51 // Knows how to handle specific states and XML. |
| 52 template <typename C> |
| 53 class PubSubStateSerializer { |
| 54 public: |
| 55 virtual ~PubSubStateSerializer() {} |
| 56 virtual XmlElement* Write(const QName& state_name, const C& state) = 0; |
| 57 virtual void Parse(const XmlElement* state_elem, C* state_out) = 0; |
| 58 }; |
| 59 |
| 60 // Knows how to create "keys" for states, which determines their |
| 61 // uniqueness. Most states are per-nick, but block is |
| 62 // per-blocker-and-blockee. This is independent of itemid, especially |
| 63 // in the case of presenter state. |
| 64 class PubSubStateKeySerializer { |
| 65 public: |
| 66 virtual ~PubSubStateKeySerializer() {} |
| 67 virtual std::string GetKey(const std::string& publisher_nick, |
| 68 const std::string& published_nick) = 0; |
| 69 }; |
| 70 |
| 71 class PublishedNickKeySerializer : public PubSubStateKeySerializer { |
| 72 public: |
| 73 virtual std::string GetKey(const std::string& publisher_nick, |
| 74 const std::string& published_nick); |
| 75 }; |
| 76 |
| 77 class PublisherAndPublishedNicksKeySerializer |
| 78 : public PubSubStateKeySerializer { |
| 79 public: |
| 80 virtual std::string GetKey(const std::string& publisher_nick, |
| 81 const std::string& published_nick); |
| 82 }; |
| 83 |
| 84 // Adapts PubSubClient to be specifically suited for pub sub call |
| 85 // states. Signals state changes and keeps track of keys, which are |
| 86 // normally nicks. |
| 87 template <typename C> |
| 88 class PubSubStateClient : public sigslot::has_slots<> { |
| 89 public: |
| 90 // Gets ownership of the serializers, but not the client. |
| 91 PubSubStateClient(const std::string& publisher_nick, |
| 92 PubSubClient* client, |
| 93 const QName& state_name, |
| 94 C default_state, |
| 95 PubSubStateKeySerializer* key_serializer, |
| 96 PubSubStateSerializer<C>* state_serializer) |
| 97 : publisher_nick_(publisher_nick), |
| 98 client_(client), |
| 99 state_name_(state_name), |
| 100 default_state_(default_state) { |
| 101 key_serializer_.reset(key_serializer); |
| 102 state_serializer_.reset(state_serializer); |
| 103 client_->SignalItems.connect( |
| 104 this, &PubSubStateClient<C>::OnItems); |
| 105 client_->SignalPublishResult.connect( |
| 106 this, &PubSubStateClient<C>::OnPublishResult); |
| 107 client_->SignalPublishError.connect( |
| 108 this, &PubSubStateClient<C>::OnPublishError); |
| 109 client_->SignalRetractResult.connect( |
| 110 this, &PubSubStateClient<C>::OnRetractResult); |
| 111 client_->SignalRetractError.connect( |
| 112 this, &PubSubStateClient<C>::OnRetractError); |
| 113 } |
| 114 |
| 115 virtual ~PubSubStateClient() {} |
| 116 |
| 117 virtual void Publish(const std::string& published_nick, |
| 118 const C& state, |
| 119 std::string* task_id_out) { |
| 120 std::string key = key_serializer_->GetKey(publisher_nick_, published_nick); |
| 121 std::string itemid = state_name_.LocalPart() + ":" + key; |
| 122 if (StatesEqual(state, default_state_)) { |
| 123 client_->RetractItem(itemid, task_id_out); |
| 124 } else { |
| 125 XmlElement* state_elem = state_serializer_->Write(state_name_, state); |
| 126 state_elem->AddAttr(QN_NICK, published_nick); |
| 127 client_->PublishItem(itemid, state_elem, task_id_out); |
| 128 } |
| 129 } |
| 130 |
| 131 sigslot::signal1<const PubSubStateChange<C>&> SignalStateChange; |
| 132 // Signal (task_id, item). item is NULL for retract. |
| 133 sigslot::signal2<const std::string&, |
| 134 const XmlElement*> SignalPublishResult; |
| 135 // Signal (task_id, item, error stanza). item is NULL for retract. |
| 136 sigslot::signal3<const std::string&, |
| 137 const XmlElement*, |
| 138 const XmlElement*> SignalPublishError; |
| 139 |
| 140 protected: |
| 141 // return false if retracted item (no info or state given) |
| 142 virtual bool ParseStateItem(const PubSubItem& item, |
| 143 StateItemInfo* info_out, |
| 144 C* state_out) { |
| 145 const XmlElement* state_elem = item.elem->FirstNamed(state_name_); |
| 146 if (state_elem == NULL) { |
| 147 return false; |
| 148 } |
| 149 |
| 150 info_out->publisher_nick = |
| 151 client_->GetPublisherNickFromPubSubItem(item.elem); |
| 152 info_out->published_nick = state_elem->Attr(QN_NICK); |
| 153 state_serializer_->Parse(state_elem, state_out); |
| 154 return true; |
| 155 } |
| 156 |
| 157 virtual bool StatesEqual(const C& state1, const C& state2) { |
| 158 return state1 == state2; |
| 159 } |
| 160 |
| 161 PubSubClient* client() { return client_; } |
| 162 const QName& state_name() { return state_name_; } |
| 163 |
| 164 private: |
| 165 void OnItems(PubSubClient* pub_sub_client, |
| 166 const std::vector<PubSubItem>& items) { |
| 167 for (std::vector<PubSubItem>::const_iterator item = items.begin(); |
| 168 item != items.end(); ++item) { |
| 169 OnItem(*item); |
| 170 } |
| 171 } |
| 172 |
| 173 void OnItem(const PubSubItem& item) { |
| 174 const std::string& itemid = item.itemid; |
| 175 StateItemInfo info; |
| 176 C new_state; |
| 177 |
| 178 bool retracted = !ParseStateItem(item, &info, &new_state); |
| 179 if (retracted) { |
| 180 bool known_itemid = |
| 181 (info_by_itemid_.find(itemid) != info_by_itemid_.end()); |
| 182 if (!known_itemid) { |
| 183 // Nothing to retract, and nothing to publish. |
| 184 // Probably a different state type. |
| 185 return; |
| 186 } else { |
| 187 info = info_by_itemid_[itemid]; |
| 188 info_by_itemid_.erase(itemid); |
| 189 new_state = default_state_; |
| 190 } |
| 191 } else { |
| 192 // TODO: Assert new key matches the known key. It |
| 193 // shouldn't change! |
| 194 info_by_itemid_[itemid] = info; |
| 195 } |
| 196 |
| 197 std::string key = key_serializer_->GetKey( |
| 198 info.publisher_nick, info.published_nick); |
| 199 bool has_old_state = (state_by_key_.find(key) != state_by_key_.end()); |
| 200 C old_state = has_old_state ? state_by_key_[key] : default_state_; |
| 201 if ((retracted && !has_old_state) || StatesEqual(new_state, old_state)) { |
| 202 // Nothing change, so don't bother signalling. |
| 203 return; |
| 204 } |
| 205 |
| 206 if (retracted || StatesEqual(new_state, default_state_)) { |
| 207 // We treat a default state similar to a retract. |
| 208 state_by_key_.erase(key); |
| 209 } else { |
| 210 state_by_key_[key] = new_state; |
| 211 } |
| 212 |
| 213 PubSubStateChange<C> change; |
| 214 if (!retracted) { |
| 215 // Retracts do not have publisher information. |
| 216 change.publisher_nick = info.publisher_nick; |
| 217 } |
| 218 change.published_nick = info.published_nick; |
| 219 change.old_state = old_state; |
| 220 change.new_state = new_state; |
| 221 SignalStateChange(change); |
| 222 } |
| 223 |
| 224 void OnPublishResult(PubSubClient* pub_sub_client, |
| 225 const std::string& task_id, |
| 226 const XmlElement* item) { |
| 227 SignalPublishResult(task_id, item); |
| 228 } |
| 229 |
| 230 void OnPublishError(PubSubClient* pub_sub_client, |
| 231 const std::string& task_id, |
| 232 const buzz::XmlElement* item, |
| 233 const buzz::XmlElement* stanza) { |
| 234 SignalPublishError(task_id, item, stanza); |
| 235 } |
| 236 |
| 237 void OnRetractResult(PubSubClient* pub_sub_client, |
| 238 const std::string& task_id) { |
| 239 // There's no point in differentiating between publish and retract |
| 240 // errors, so we simplify by making them both signal a publish |
| 241 // result. |
| 242 const XmlElement* item = NULL; |
| 243 SignalPublishResult(task_id, item); |
| 244 } |
| 245 |
| 246 void OnRetractError(PubSubClient* pub_sub_client, |
| 247 const std::string& task_id, |
| 248 const buzz::XmlElement* stanza) { |
| 249 // There's no point in differentiating between publish and retract |
| 250 // errors, so we simplify by making them both signal a publish |
| 251 // error. |
| 252 const XmlElement* item = NULL; |
| 253 SignalPublishError(task_id, item, stanza); |
| 254 } |
| 255 |
| 256 std::string publisher_nick_; |
| 257 PubSubClient* client_; |
| 258 const QName state_name_; |
| 259 C default_state_; |
| 260 std::unique_ptr<PubSubStateKeySerializer> key_serializer_; |
| 261 std::unique_ptr<PubSubStateSerializer<C> > state_serializer_; |
| 262 // key => state |
| 263 std::map<std::string, C> state_by_key_; |
| 264 // itemid => StateItemInfo |
| 265 std::map<std::string, StateItemInfo> info_by_itemid_; |
| 266 |
| 267 RTC_DISALLOW_COPY_AND_ASSIGN(PubSubStateClient); |
| 268 }; |
| 269 } // namespace buzz |
| 270 |
| 271 #endif // WEBRTC_LIBJINGLE_XMPP_PUBSUBSTATECLIENT_H_ |
OLD | NEW |