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 import printactions
41
42
43 from backends.printing import X2goClientPrinting as _X2goClientPrinting
44
45 if defaults.X2GOCLIENT_OS != 'Windows':
46 from x2go_exceptions import WindowsError
47
48 from defaults import X2GO_PRINTING_FILENAME as _X2GO_PRINTING_FILENAME
52 """\
53 If X2go printing is supported in a particular L{X2goSession} instance
54 this class provides a sub-thread for handling incoming X2go print jobs.
55
56 """
57 print_action = None
58
59 spooldir = None
60 active_jobs = {}
61 job_history = []
62
63 - def __init__(self,
64 profile_name='UNKNOWN',
65 session_name='UNKNOWN',
66 spool_dir=None,
67 print_action=None,
68 print_action_args={},
69 client_instance=None,
70 printing_backend=_X2goClientPrinting,
71 logger=None,
72 loglevel=log.loglevel_DEFAULT):
73 """\
74 @param profile_name: name of the session profile this print queue belongs to
75 @type profile_name: C{str}
76 @param spool_dir: local spool directory for incoming print job files
77 @type spool_dir: C{str}
78 @param print_action: name or instance of either of the possible X2go print action classes
79 @type print_action: C{str} or instance
80 @param print_action_args: depending of the chosen C{print_action} this dictionary may contain different
81 values; the C{print_action_args} will be passed on to the X2go print action instance constructor, so
82 refer to either of these: L{X2goPrintActionPDFVIEW}, L{X2goPrintActionPRINT} et al.
83 @param client_instance: the underlying L{X2goClient} instance
84 @type client_instance: C{instance}
85 @param printing_backend: the client printing configuration backend class
86 @type printing_backend: C{instance}
87 @param logger: you can pass an L{X2goLogger} object to the
88 L{X2goPrintQueue} constructor
89 @type logger: C{instance}
90 @param loglevel: if no L{X2goLogger} object has been supplied a new one will be
91 constructed with the given loglevel
92 @type loglevel: C{int}
93
94 """
95 if logger is None:
96 self.logger = log.X2goLogger(loglevel=loglevel)
97 else:
98 self.logger = copy.deepcopy(logger)
99 self.logger.tag = __NAME__
100
101 self.profile_name = profile_name
102 self.session_name = session_name
103 self.spool_dir = spool_dir
104 self.client_instance = client_instance
105 self.client_rootdir = client_instance.get_client_rootdir()
106 self.printing_backend = printing_backend
107 if print_action is not None:
108 self.set_print_action(print_action, client_instance=self.client_instance, logger=logger, **print_action_args)
109 threading.Thread.__init__(self)
110 self.daemon = True
111 self._accept_jobs = True
112
114 """\
115 Class destructor.
116
117 """
118 self.stop_thread()
119
121 """\
122 Prevent acceptance of new incoming print jobs. The processing of print jobs that
123 are currently still active will be completed, though.
124
125 """
126 if self._accept_jobs == True:
127 self._accept_jobs = False
128 self.logger('paused thread: %s' % repr(self), loglevel=log.loglevel_DEBUG)
129
131 """\
132 Resume operation of the X2go print spooler and continue accepting new incoming
133 print jobs.
134
135 """
136 if self._accept_jobs == False:
137 self._accept_jobs = True
138 self.logger('resumed thread: %s' % repr(self), loglevel=log.loglevel_DEBUG)
139
141 """\
142 Stops this L{X2goPrintQueue} thread completely.
143
144 """
145 self.pause()
146 self._keepalive = False
147 self.logger('stopping thread: %s' % repr(self), loglevel=log.loglevel_DEBUG)
148
149 @property
151
152 if os.path.exists(self.spool_dir):
153 l = os.listdir(self.spool_dir)
154 job_files = [ jf for jf in l if jf.endswith('.ready') ]
155 jobs = []
156 for _job_file in job_files:
157 j = open(os.path.join(self.spool_dir, _job_file), 'r')
158 content = j.read()
159 try:
160 (pdf_filename, job_title) = content.split('\n')[0:2]
161 except ValueError:
162 pdf_filename = content
163 job_title = 'X2go Print Job'
164 j.close()
165 jobs.append((_job_file, pdf_filename, job_title))
166 return [ j for j in jobs if j[1] not in self.active_jobs.keys() ]
167 else:
168 return []
169
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{instance}
239 @param logger: the L{X2goPrintQueue}'s logging instance
240 @type logger: C{instance}
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