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 "webrtc/libjingle/xmllite/qname.h" | |
20 #include "webrtc/libjingle/xmllite/xmlelement.h" | |
21 #include "webrtc/libjingle/xmpp/constants.h" | |
22 #include "webrtc/libjingle/xmpp/jid.h" | |
23 #include "webrtc/libjingle/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 |