1 /* |
|
2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * |
|
5 * This program is free software: you can redistribute it and/or modify |
|
6 * it under the terms of the GNU Lesser General Public License as published by |
|
7 * the Free Software Foundation, version 2.1 of the License. |
|
8 * |
|
9 * This program is distributed in the hope that it will be useful, |
|
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 * GNU Lesser General Public License for more details. |
|
13 * |
|
14 * You should have received a copy of the GNU Lesser General Public License |
|
15 * along with this program. If not, |
|
16 * see "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html/". |
|
17 * |
|
18 * Description: |
|
19 * |
|
20 */ |
|
21 |
|
22 #include "xqservicelog.h" |
|
23 |
|
24 #include "xqservicechannel.h" |
|
25 |
|
26 #include "xqservicethreaddata.h" |
|
27 #include "xqserviceipcclient.h" |
|
28 |
|
29 #include <xqserviceutil.h> |
|
30 #include <xqsharablefile.h> |
|
31 |
|
32 |
|
33 |
|
34 /*! |
|
35 \class XQServiceChannel |
|
36 \inpublicgroup QtBaseModule |
|
37 \ingroup qws |
|
38 |
|
39 \brief The XQServiceChannel class provides communication capabilities |
|
40 between clients. |
|
41 |
|
42 XQSERVICE is a many-to-many communication protocol for transferring |
|
43 messages on various channels. A channel is identified by a name, |
|
44 and anyone who wants to can listen to it. The XQSERVICE protocol allows |
|
45 clients to communicate both within the same address space and |
|
46 between different processes, but it is currently only available |
|
47 for \l {Qt for Embedded Linux} (on X11 and Windows we are exploring the use |
|
48 of existing standards such as DCOP and COM). |
|
49 |
|
50 Typically, XQServiceChannel is either used to send messages to a |
|
51 channel using the provided static functions, or to listen to the |
|
52 traffic on a channel by deriving from the class to take advantage |
|
53 of the provided functionality for receiving messages. |
|
54 |
|
55 XQServiceChannel provides a couple of static functions which are usable |
|
56 without an object: The send() function, which sends the given |
|
57 message and data on the specified channel, and the isRegistered() |
|
58 function which queries the server for the existence of the given |
|
59 channel. |
|
60 |
|
61 In addition, the XQServiceChannel class provides the channel() function |
|
62 which returns the name of the object's channel, the virtual |
|
63 receive() function which allows subclasses to process data |
|
64 received from their channel, and the received() signal which is |
|
65 emitted with the given message and data when a XQServiceChannel |
|
66 subclass receives a message from its channel. |
|
67 |
|
68 \sa XQServiceServer, {Running Qt for Embedded Linux Applications} |
|
69 */ |
|
70 |
|
71 /*! |
|
72 Constructs a XQService channel with the given \a parent, and registers it |
|
73 with the server using the given \a channel name. |
|
74 \param channel Channel name. |
|
75 \param isServer |
|
76 \param parent Parent of this object. |
|
77 \sa isRegistered(), channel() |
|
78 */ |
|
79 |
|
80 XQServiceChannel::XQServiceChannel(const QString& channel, bool isServer, QObject *parent) |
|
81 : QObject(parent) |
|
82 { |
|
83 XQSERVICE_DEBUG_PRINT("XQServiceChannel::XQServiceChannel"); |
|
84 XQSERVICE_DEBUG_PRINT("channel: %s, isServer: %d", qPrintable(channel), isServer ); |
|
85 d = new XQServiceChannelPrivate(this, channel, isServer); |
|
86 d->ref.ref(); |
|
87 |
|
88 XQServiceThreadData *td = XQService::serviceThreadData(); |
|
89 |
|
90 // do we need a new channel list ? |
|
91 XQServiceClientMap::Iterator it = td->clientMap.find(channel); |
|
92 if (it != td->clientMap.end()) { |
|
93 XQSERVICE_DEBUG_PRINT("Channel exits"); |
|
94 it.value().append(XQServiceChannelPrivatePointer(d)); |
|
95 return; |
|
96 } |
|
97 XQSERVICE_DEBUG_PRINT("New channel"); |
|
98 it = td->clientMap.insert(channel, QList<XQServiceChannelPrivatePointer>()); |
|
99 it.value().append(XQServiceChannelPrivatePointer(d)); |
|
100 } |
|
101 |
|
102 /*! |
|
103 Destroys the client's end of the channel and notifies the server |
|
104 that the client has closed its connection. The server will keep |
|
105 the channel open until the last registered client detaches. |
|
106 |
|
107 \sa XQServiceChannel() |
|
108 */ |
|
109 |
|
110 XQServiceChannel::~XQServiceChannel() |
|
111 { |
|
112 XQSERVICE_DEBUG_PRINT("XQServiceChannel::~XQServiceChannel"); |
|
113 XQServiceThreadData *td = XQService::serviceThreadData(); |
|
114 |
|
115 XQServiceClientMap::Iterator it = td->clientMap.find(d->channel); |
|
116 Q_ASSERT(it != td->clientMap.end()); |
|
117 it.value().removeAll(XQServiceChannelPrivatePointer(d)); |
|
118 // still any clients connected locally ? |
|
119 if (it.value().isEmpty()) { |
|
120 if (td->hasClientConnection(d->channel)) |
|
121 td->closeClientConnection(d->channel); |
|
122 td->clientMap.remove(d->channel); |
|
123 } |
|
124 |
|
125 // Dereference the private data structure. It may stay around |
|
126 // for a little while longer if it is in use by sendLocally(). |
|
127 d->object = 0; |
|
128 if (!d->ref.deref()) |
|
129 delete d; |
|
130 } |
|
131 |
|
132 bool XQServiceChannel::connectChannel() |
|
133 { |
|
134 XQSERVICE_DEBUG_PRINT("XQServiceChannel::connectChannel"); |
|
135 bool ret = true; |
|
136 |
|
137 XQServiceThreadData *td = XQService::serviceThreadData(); |
|
138 |
|
139 if (!td->hasClientConnection(d->channel)) { |
|
140 XQSERVICE_DEBUG_PRINT("Create new client connection (1)"); |
|
141 ret = td->createClientConnection(d->channel,d->server); |
|
142 } |
|
143 return ret; |
|
144 } |
|
145 |
|
146 /*! |
|
147 Returns the name of the channel. |
|
148 |
|
149 \sa XQServiceChannel() |
|
150 */ |
|
151 |
|
152 QString XQServiceChannel::channel() const |
|
153 { |
|
154 XQSERVICE_DEBUG_PRINT("XQServiceChannel::channel"); |
|
155 XQSERVICE_DEBUG_PRINT("channel: %s", qPrintable(d->channel)); |
|
156 return d->channel; |
|
157 } |
|
158 |
|
159 /*! |
|
160 \fn void XQServiceChannel::receive(const QString& message, const QByteArray &data) |
|
161 |
|
162 This virtual function allows subclasses of XQServiceChannel to process |
|
163 the given \a message and \a data received from their channel. The default |
|
164 implementation emits the received() signal. |
|
165 |
|
166 Note that the format of the given \a data has to be well defined |
|
167 in order to extract the information it contains. In addition, it |
|
168 is recommended to use the DCOP convention. This is not a |
|
169 requirement, but you must ensure that the sender and receiver |
|
170 agree on the argument types. |
|
171 |
|
172 Example: |
|
173 |
|
174 \code |
|
175 void MyClass::receive(const QString &message, const QByteArray &data) |
|
176 { |
|
177 QDataStream in(data); |
|
178 if (message == "execute(QString,QString)") { |
|
179 QString cmd; |
|
180 QString arg; |
|
181 in >> cmd >> arg; |
|
182 ... |
|
183 } else if (message == "delete(QString)") { |
|
184 QString fileName; |
|
185 in >> fileName; |
|
186 ... |
|
187 } else { |
|
188 ... |
|
189 } |
|
190 } |
|
191 \endcode |
|
192 |
|
193 This example assumes that the \c message is a DCOP-style function |
|
194 signature and the \c data contains the function's arguments. |
|
195 |
|
196 \sa send() |
|
197 */ |
|
198 QVariant XQServiceChannel::receive(const QString& msg, const QByteArray &data, const XQSharableFile &sf ) |
|
199 { |
|
200 XQSERVICE_DEBUG_PRINT("XQServiceChannel::receive"); |
|
201 XQSERVICE_DEBUG_PRINT("msg: %s, data: %s", qPrintable(msg), data.constData()); |
|
202 emit received(msg, data,sf); |
|
203 return QVariant(); |
|
204 } |
|
205 |
|
206 void XQServiceChannel::commandReceive(const XQServiceCommand cmd) |
|
207 { |
|
208 XQSERVICE_DEBUG_PRINT("XQServiceChannel::commandReceive %d", cmd); |
|
209 emit commandReceived(cmd); |
|
210 } |
|
211 |
|
212 /*! |
|
213 \fn void XQServiceChannel::received(const QString& message, const QByteArray &data) |
|
214 |
|
215 This signal is emitted with the given \a message and \a data whenever the |
|
216 receive() function gets incoming data. |
|
217 |
|
218 \sa receive() |
|
219 */ |
|
220 |
|
221 /*! |
|
222 \fn bool XQServiceChannel::send(const QString& channel, const QString& message, |
|
223 const QByteArray &data) |
|
224 |
|
225 Sends the given \a message on the specified \a channel with the |
|
226 given \a data. The message will be distributed to all clients |
|
227 subscribed to the channel. Returns true if the message is sent |
|
228 successfully; otherwise returns false. |
|
229 |
|
230 It is recommended to use the DCOP convention. This is not a |
|
231 requirement, but you must ensure that the sender and receiver |
|
232 agree on the argument types. |
|
233 |
|
234 Note that QDataStream provides a convenient way to fill the byte |
|
235 array with auxiliary data. |
|
236 |
|
237 Example: |
|
238 |
|
239 \code |
|
240 QByteArray data; |
|
241 QDataStream out(&data, QIODevice::WriteOnly); |
|
242 out << QString("cat") << QString("file.txt"); |
|
243 XQServiceChannel::send("System/Shell", "execute(QString,QString)", data); |
|
244 \endcode |
|
245 |
|
246 Here the channel is \c "System/Shell". The \c message is an |
|
247 arbitrary string, but in the example we've used the DCOP |
|
248 convention of passing a function signature. Such a signature is |
|
249 formatted as \c "functionname(types)" where \c types is a list of |
|
250 zero or more comma-separated type names, with no whitespace, no |
|
251 consts and no pointer or reference marks, i.e. no "*" or "&". |
|
252 |
|
253 \sa receive() |
|
254 */ |
|
255 |
|
256 bool XQServiceChannel::send(const QString& channel, const QString& msg, |
|
257 const QByteArray &data, QVariant &retValue, |
|
258 bool sync, XQServiceRequestCompletedAsync* rc) |
|
259 { |
|
260 XQSERVICE_DEBUG_PRINT("XQServiceChannel::send(1). No user data"); |
|
261 // Delegate to actual send. |
|
262 // No user data argument present in this version |
|
263 return send(channel,msg,data,retValue,sync,rc,NULL); |
|
264 } |
|
265 |
|
266 |
|
267 |
|
268 bool XQServiceChannel::send(const QString& channel, const QString& msg, |
|
269 const QByteArray &data, QVariant &retValue, |
|
270 bool sync, XQServiceRequestCompletedAsync* rc, |
|
271 const void* userData) |
|
272 { |
|
273 XQSERVICE_DEBUG_PRINT("XQServiceChannel::send(2) start"); |
|
274 XQSERVICE_DEBUG_PRINT("\t channel: %s, msg: %s", qPrintable(channel), qPrintable(msg)); |
|
275 XQSERVICE_DEBUG_PRINT("\t data: %s, sync: %d", data.constData(), sync); |
|
276 bool ret=true; |
|
277 |
|
278 if (!XQService::serviceThreadData()->hasClientConnection(channel)) { |
|
279 XQSERVICE_DEBUG_PRINT("\t Create new client connection (2)"); |
|
280 ret = XQService::serviceThreadData()->createClientConnection(channel,false,sync,rc, userData); |
|
281 XQSERVICE_DEBUG_PRINT("\t creation succeeded: %d", ret); |
|
282 } |
|
283 if (ret) { |
|
284 XQSERVICE_DEBUG_PRINT("\t ret = true"); |
|
285 XQServiceIpcClient *cl = XQService::serviceThreadData()->clientConnection(channel); |
|
286 QByteArray retData ; |
|
287 ret = cl ? cl->send(channel, msg, data, retData) : false; |
|
288 if (sync) { |
|
289 retValue = XQServiceThreadData::deserializeRetData(retData); |
|
290 } |
|
291 } |
|
292 XQSERVICE_DEBUG_PRINT("\t ret: %d", ret); |
|
293 XQSERVICE_DEBUG_PRINT("XQServiceChannel::send(2) end"); |
|
294 return ret; |
|
295 } |
|
296 |
|
297 |
|
298 bool XQServiceChannel::cancelPendingSend(const QString& channel) |
|
299 { |
|
300 XQSERVICE_DEBUG_PRINT("XQServiceChannel::cancelPendingSend start"); |
|
301 XQSERVICE_DEBUG_PRINT("\t channel: %s", qPrintable(channel)); |
|
302 bool ret=true; |
|
303 |
|
304 if (ret) { |
|
305 XQServiceIpcClient *cl = XQService::serviceThreadData()->clientConnection(channel); |
|
306 XQSERVICE_DEBUG_PRINT("\t XQService::serviceThreadData()->clientConnection(channel): %d", cl); |
|
307 XQSERVICE_DEBUG_PRINT("\t cl->cancelPendingSend(%s)", qPrintable(channel)); |
|
308 ret = cl ? cl->cancelPendingSend(channel) : false; |
|
309 } |
|
310 |
|
311 XQSERVICE_DEBUG_PRINT("\t ret: %d", ret); |
|
312 XQSERVICE_DEBUG_PRINT("XQServiceChannel::cancelPendingSend end"); |
|
313 return ret; |
|
314 } |
|
315 |
|
316 /*! |
|
317 \internal |
|
318 Client side: distribute received event to the XQService instance managing the |
|
319 channel. |
|
320 */ |
|
321 QVariant XQServiceChannel::sendLocally(const QString& ch, const QString& msg, |
|
322 const QByteArray &data, const XQSharableFile &sf ) |
|
323 { |
|
324 XQSERVICE_DEBUG_PRINT("XQServiceChannel::sendLocally"); |
|
325 XQSERVICE_DEBUG_PRINT("channel: %s, msg: %s", qPrintable(ch), qPrintable(msg)); |
|
326 XQSERVICE_DEBUG_PRINT("data: %s", data.constData()); |
|
327 QVariant ret; |
|
328 |
|
329 // feed local clients with received data |
|
330 XQServiceThreadData *td = XQService::serviceThreadData(); |
|
331 QList<XQServiceChannelPrivatePointer> clients = td->clientMap[ch]; |
|
332 for (int i = 0; i < clients.size(); ++i) { |
|
333 XQServiceChannelPrivate *channel = clients.at(i).data(); |
|
334 if (channel->object) |
|
335 ret = channel->object->receive(msg, data,sf ); |
|
336 } |
|
337 |
|
338 #ifdef XQSERVICE_DEBUG |
|
339 QString s = ret.toString(); |
|
340 int len=s.length(); |
|
341 XQSERVICE_DEBUG_PRINT("sendLocally ret: type=%s,len=%d,value(max.1024)=%s", |
|
342 ret.typeName(),len,qPrintable(s.left(1024))); |
|
343 #endif |
|
344 return ret ; |
|
345 } |
|
346 |
|
347 int XQServiceChannel::latestError() |
|
348 { |
|
349 XQSERVICE_DEBUG_PRINT("XQServiceChannel::latestError"); |
|
350 return XQService::serviceThreadData()->latestError(); |
|
351 } |
|
352 |
|
353 void XQServiceChannel::sendCommand(const QString& ch,const XQServiceCommand cmd) |
|
354 { |
|
355 XQSERVICE_DEBUG_PRINT("XQServiceChannel::sendCommand"); |
|
356 XQSERVICE_DEBUG_PRINT("channel: %s, cmd: %d", qPrintable(ch), cmd); |
|
357 // feed local clients with received data |
|
358 XQServiceThreadData *td = XQService::serviceThreadData(); |
|
359 QList<XQServiceChannelPrivatePointer> clients = td->clientMap[ch]; |
|
360 for (int i = 0; i < clients.size(); ++i) { |
|
361 XQServiceChannelPrivate *channel = clients.at(i).data(); |
|
362 if (channel->object) |
|
363 channel->object->commandReceive(cmd); |
|
364 } |
|
365 } |
|
366 |
|