1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 """\
21 L{X2goPrintQueue} sets up a thread that listens for incoming print jobs.
22
23 For each incoming print job in an X2Go session's spool directory an
24 individual thread is started (L{X2goPrintJob}) that handles the processing
25 of the incoming print job.
26
27 """
28 __NAME__ = 'x2goprintqueue-pylib'
29
30
31 import os
32 import copy
33 import threading
34 import gevent
35
36
37 import defaults
38 import utils
39 import log
40
41
42 from backends.printing import X2goClientPrinting as _X2goClientPrinting
43
44 from defaults import X2GO_PRINTING_FILENAME as _X2GO_PRINTING_FILENAME
48 """\
49 If X2Go printing is supported in a particular L{X2goSession} instance
50 this class provides a sub-thread for handling incoming X2Go print jobs.
51
52 """
53 print_action = None
54
55 spooldir = None
56 active_jobs = {}
57 job_history = []
58
59 - def __init__(self,
60 profile_name='UNKNOWN',
61 session_name='UNKNOWN',
62 spool_dir=None,
63 print_action=None,
64 print_action_args={},
65 client_instance=None,
66 printing_backend=_X2goClientPrinting,
67 logger=None,
68 loglevel=log.loglevel_DEFAULT):
69 """\
70 @param profile_name: name of the session profile this print queue belongs to
71 @type profile_name: C{str}
72 @param spool_dir: local spool directory for incoming print job files
73 @type spool_dir: C{str}
74 @param print_action: name or instance of either of the possible X2Go print action classes
75 @type print_action: C{str} or instance
76 @param print_action_args: depending of the chosen C{print_action} this dictionary may contain different
77 values; the C{print_action_args} will be passed on to the X2Go print action instance constructor, so
78 refer to either of these: L{X2goPrintActionPDFVIEW}, L{X2goPrintActionPRINT} et al.
79 @param client_instance: the underlying L{X2goClient} instance
80 @type client_instance: C{obj}
81 @param printing_backend: the client printing configuration backend class
82 @type printing_backend: C{obj}
83 @param logger: you can pass an L{X2goLogger} object to the
84 L{X2goPrintQueue} constructor
85 @type logger: C{obj}
86 @param loglevel: if no L{X2goLogger} object has been supplied a new one will be
87 constructed with the given loglevel
88 @type loglevel: C{int}
89
90 """
91 if logger is None:
92 self.logger = log.X2goLogger(loglevel=loglevel)
93 else:
94 self.logger = copy.deepcopy(logger)
95 self.logger.tag = __NAME__
96
97 self.profile_name = profile_name
98 self.session_name = session_name
99 self.spool_dir = spool_dir
100 if self.spool_dir: self.spool_dir = os.path.normpath(self.spool_dir)
101 self.client_instance = client_instance
102 self.client_rootdir = client_instance.get_client_rootdir()
103 self.printing_backend = printing_backend
104 if print_action is not None:
105 self.set_print_action(print_action, client_instance=self.client_instance, logger=logger, **print_action_args)
106 threading.Thread.__init__(self)
107 self.daemon = True
108 self._accept_jobs = True
109
111 """\
112 Class destructor.
113
114 """
115 self.stop_thread()
116
118 """\
119 Prevent acceptance of new incoming print jobs. The processing of print jobs that
120 are currently still active will be completed, though.
121
122 """
123 if self._accept_jobs == True:
124 self._accept_jobs = False
125 self.logger('paused thread: %s' % repr(self), loglevel=log.loglevel_DEBUG)
126
128 """\
129 Resume operation of the X2Go print spooler and continue accepting new incoming
130 print jobs.
131
132 """
133 if self._accept_jobs == False:
134 self._accept_jobs = True
135 self.logger('resumed thread: %s' % repr(self), loglevel=log.loglevel_DEBUG)
136
138 """\
139 Stops this L{X2goPrintQueue} thread completely.
140
141 """
142 self.pause()
143 self._keepalive = False
144 self.logger('stopping thread: %s' % repr(self), loglevel=log.loglevel_DEBUG)
145
146 @property
148
149 if os.path.exists(self.spool_dir):
150 l = os.listdir(self.spool_dir)
151 job_files = [ jf for jf in l if jf.endswith('.ready') ]
152 jobs = []
153 for _job_file in job_files:
154 j = open(os.path.join(self.spool_dir, _job_file), 'r')
155 content = j.read()
156 try:
157 (pdf_filename, job_title) = content.split('\n')[0:2]
158 except ValueError:
159 pdf_filename = content
160 job_title = 'X2Go Print Job'
161 j.close()
162 jobs.append((_job_file, pdf_filename, job_title))
163 return [ j for j in jobs if j[1] not in self.active_jobs.keys() ]
164 else:
165 return []
166
168 """\
169 Modify the print action of this L{X2goPrintQueue} thread during runtime. The
170 change of print action will be valid for the next incoming print job.
171
172 As kwargs you can pass arguments for the print action class to be set. Refer
173 to the class descriptions of L{X2goPrintActionDIALOG}, L{X2goPrintActionPDFVIEW},
174 L{X2goPrintActionPRINT}, etc.
175
176 @param print_action: new print action to be valid for incoming print jobs
177 @type print_action: C{str} or C{class}
178 @param kwargs: extra options for the specified print action
179 @type kwargs: C{dict}
180
181 """
182 if print_action in defaults.X2GO_PRINT_ACTIONS.keys():
183 print_action = defaults.X2GO_PRINT_ACTIONS[print_action]
184
185 if print_action in defaults.X2GO_PRINT_ACTIONS.values():
186 self.print_action = eval ('printactions.%s(**kwargs)' % print_action)
187
189 """\
190 Start this L{X2goPrintQueue} thread...
191
192 """
193 self.logger('starting print queue thread: %s' % repr(self), loglevel=log.loglevel_DEBUG)
194
195 self._keepalive = True
196 while self._keepalive:
197
198 while self._accept_jobs:
199
200 if self._incoming_print_jobs:
201
202 for _job in self._incoming_print_jobs:
203 self.logger('processing incoming X2Go print job: %s' % _job[1], loglevel=log.loglevel_NOTICE)
204 _new_printjob_thread = X2goPrintJob(target=x2go_printjob_handler,
205 kwargs={
206 'job_file': _job[0],
207 'pdf_file': _job[1],
208 'job_title': _job[2],
209 'print_action': self.print_action,
210 'parent_thread': self,
211 'logger': self.logger,
212 }
213 )
214 self.active_jobs['%s' % _job[1]] = _new_printjob_thread
215 _new_printjob_thread.start()
216
217 gevent.sleep(3)
218
219 gevent.sleep(1)
220
221
222 -def x2go_printjob_handler(job_file=None, pdf_file=None, job_title=None, print_action=None, parent_thread=None, logger=None, ):
223 """\
224 This function is called as a handler function for each incoming X2Go print job
225 represented by the class L{X2goPrintJob}.
226
227 The handler function will (re-)read the »printing« configuration file (if no
228 explicit C{print_action} is passed to this function...). It then will
229 execute the C{<print_action>.do_print()} command.
230
231 @param pdf_file: PDF file name as placed in to the X2Go spool directory
232 @type pdf_file: C{str}
233 @param job_title: human readable print job title
234 @type job_title: C{str}
235 @param print_action: an instance of either of the possible C{X2goPrintActionXXX} classes
236 @type print_action: C{X2goPrintActionXXX} nstance
237 @param parent_thread: the L{X2goPrintQueue} thread that actually created this handler's L{X2goPrintJob} instance
238 @type parent_thread: C{obj}
239 @param logger: the L{X2goPrintQueue}'s logging instance
240 @type logger: C{obj}
241
242 """
243 if print_action is None:
244 if parent_thread.client_instance is not None and parent_thread.client_instance.has_custom_client_rootdir:
245 _printing = parent_thread.printing_backend(config_files=[os.path.join(parent_thread.client_instance.get_client_rootdir(), _X2GO_PRINTING_FILENAME)],
246 client_instance=parent_thread.client_instance,
247 logger=logger
248 )
249 else:
250 _printing = parent_thread.printing_backend(client_instance=parent_thread.client_instance,
251 logger=logger
252 )
253
254 print_action = _printing.print_action
255 print_action.profile_name = parent_thread.profile_name
256 print_action.session_name = parent_thread.session_name
257
258 logger('action for printing is: %s' % print_action, loglevel=log.loglevel_DEBUG)
259 print_action.do_print(pdf_file=os.path.normpath(os.path.join(parent_thread.spool_dir, pdf_file)),
260 job_title=job_title,
261 spool_dir=parent_thread.spool_dir,
262 )
263
264 logger('removing print job files for %s' % pdf_file, loglevel=log.loglevel_DEBUG)
265
266 utils.patiently_remove_file(parent_thread.spool_dir, job_file)
267 logger('removed print job file %s' % job_file, loglevel=log.loglevel_DEBUG)
268 utils.patiently_remove_file(parent_thread.spool_dir, pdf_file)
269 logger('removed print pdf file %s' % pdf_file, loglevel=log.loglevel_DEBUG)
270
271 del parent_thread.active_jobs['%s' % pdf_file]
272 parent_thread.job_history.append(pdf_file)
273
274
275
276 if len(parent_thread.job_history) > 100:
277 parent_thread.job_history = parent_thread.job_history[-100:]
278
281 """\
282 For each X2Go print job we create a sub-thread that let's
283 the print job be processed in the background.
284
285 As a handler for this class the function L{x2go_printjob_handler()}
286 is used.
287
288 """
290 """\
291 Construct the X2Go print job thread...
292
293 All parameters (**kwargs) are passed through to the constructor
294 of C{threading.Thread()}.
295
296 """
297 threading.Thread.__init__(self, **kwargs)
298 self.daemon = True
299