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