7 Module implementing the single application server and client. |
7 Module implementing the single application server and client. |
8 """ |
8 """ |
9 |
9 |
10 from __future__ import unicode_literals |
10 from __future__ import unicode_literals |
11 |
11 |
|
12 import json |
|
13 |
12 from PyQt5.QtCore import QByteArray |
14 from PyQt5.QtCore import QByteArray |
13 from PyQt5.QtNetwork import QLocalServer, QLocalSocket |
15 from PyQt5.QtNetwork import QLocalServer, QLocalSocket |
|
16 |
|
17 from E5Gui import E5MessageBox |
|
18 |
|
19 import Utilities |
14 |
20 |
15 |
21 |
16 class SingleApplicationServer(QLocalServer): |
22 class SingleApplicationServer(QLocalServer): |
17 """ |
23 """ |
18 Class implementing the single application server base class. |
24 Class implementing the single application server base class. |
39 """ |
45 """ |
40 Private slot to handle a new connection. |
46 Private slot to handle a new connection. |
41 """ |
47 """ |
42 sock = self.nextPendingConnection() |
48 sock = self.nextPendingConnection() |
43 |
49 |
44 # If we already have a connection, refuse this one. It will be closed |
50 # If we already have a connection, refuse this one. It will be closed |
45 # automatically. |
51 # automatically. |
46 if self.qsock is not None: |
52 if self.qsock is not None: |
47 return |
53 return |
48 |
54 |
49 self.qsock = sock |
55 self.qsock = sock |
50 |
56 |
51 self.qsock.readyRead.connect(self.__parseLine) |
57 self.qsock.readyRead.connect(self.__receiveJson) |
52 self.qsock.disconnected.connect(self.__disconnected) |
58 self.qsock.disconnected.connect(self.__disconnected) |
53 |
59 |
54 def __parseLine(self): |
60 def __receiveJson(self): |
55 """ |
61 """ |
56 Private method to handle data from the client. |
62 Private method to receive the data from the client. |
57 """ |
63 """ |
58 while self.qsock and self.qsock.canReadLine(): |
64 while self.qsock and self.qsock.canReadLine(): |
59 line = bytes(self.qsock.readLine()).decode() |
65 line = bytes(self.qsock.readLine()).decode() |
60 |
66 |
61 ## print(line) ##debug |
67 ## print(line) ##debug |
62 |
68 |
63 eoc = line.find('<') + 1 |
69 try: |
64 |
70 commandDict = json.loads(line.strip()) |
65 boc = line.find('>') |
71 except (TypeError, ValueError) as err: |
66 if boc >= 0 and eoc > boc: |
72 E5MessageBox.critical( |
67 # handle the command sent by the client. |
73 None, |
68 cmd = line[boc:eoc] |
74 self.tr("Single Application Protocol Error"), |
69 params = line[eoc:-1] |
75 self.tr("""<p>The response received from the single""" |
70 |
76 """ application client could not be decoded.""" |
71 self.handleCommand(cmd, params) |
77 """ Please report this issue with the received""" |
|
78 """ data to the eric bugs email address.</p>""" |
|
79 """<p>Error: {0}</p>""" |
|
80 """<p>Data:<br/>{0}</p>""").format( |
|
81 str(err), Utilities.html_encode(line.strip())), |
|
82 E5MessageBox.StandardButtons( |
|
83 E5MessageBox.Ok)) |
|
84 return |
|
85 |
|
86 command = commandDict["command"] |
|
87 arguments = commandDict["arguments"] |
|
88 |
|
89 self.handleCommand(command, arguments) |
72 |
90 |
73 def __disconnected(self): |
91 def __disconnected(self): |
74 """ |
92 """ |
75 Private method to handle the closure of the socket. |
93 Private method to handle the closure of the socket. |
76 """ |
94 """ |
86 |
104 |
87 self.qsock = None |
105 self.qsock = None |
88 |
106 |
89 self.close() |
107 self.close() |
90 |
108 |
91 def handleCommand(self, cmd, params): |
109 def handleCommand(self, command, arguments): |
92 """ |
110 """ |
93 Public slot to handle the command sent by the client. |
111 Public slot to handle the command sent by the client. |
94 |
112 |
95 <b>Note</b>: This method must be overridden by subclasses. |
113 <b>Note</b>: This method must be overridden by subclasses. |
96 |
114 |
97 @param cmd commandstring (string) |
115 @param command command sent by the client |
98 @param params parameterstring (string) |
116 @type str |
|
117 @param arguments list of command arguments |
|
118 @type list of str |
99 @exception RuntimeError raised to indicate that this method must be |
119 @exception RuntimeError raised to indicate that this method must be |
100 implemented by a subclass |
120 implemented by a subclass |
101 """ |
121 """ |
102 raise RuntimeError("'handleCommand' must be overridden") |
122 raise RuntimeError("'handleCommand' must be overridden") |
103 |
123 |
154 @exception RuntimeError raised to indicate that this method must be |
174 @exception RuntimeError raised to indicate that this method must be |
155 implemented by a subclass |
175 implemented by a subclass |
156 """ |
176 """ |
157 raise RuntimeError("'processArgs' must be overridden") |
177 raise RuntimeError("'processArgs' must be overridden") |
158 |
178 |
159 def sendCommand(self, cmd): |
179 def sendCommand(self, command, arguments): |
160 """ |
180 """ |
161 Public method to send the command to the application server. |
181 Public method to send the command to the application server. |
162 |
182 |
163 @param cmd command to be sent (string) |
183 @param command command to be sent to the server |
|
184 @type str |
|
185 @param arguments list of command arguments |
|
186 @type list of str |
164 """ |
187 """ |
165 if self.connected: |
188 if self.connected: |
166 self.sock.write(QByteArray(cmd.encode())) |
189 commandDict = { |
|
190 "command": command, |
|
191 "arguments": arguments, |
|
192 } |
|
193 self.sock.write(QByteArray( |
|
194 "{0}\n".format(json.dumps(commandDict)).encode() |
|
195 )) |
167 self.sock.flush() |
196 self.sock.flush() |
168 |
197 |
169 def errstr(self): |
198 def errstr(self): |
170 """ |
199 """ |
171 Public method to return a meaningful error string for the last error. |
200 Public method to return a meaningful error string for the last error. |