Package x2go :: Module printqueue

[frames] | no frames]

# Source Code for Module x2go.printqueue

      1  # -*- coding: utf-8 -*-
      2
      3  # Copyright (C) 2010-2016 by Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
      4  #
      5  # Python X2Go is free software; you can redistribute it and/or modify
      6  # it under the terms of the GNU Affero General Public License as published by
      7  # the Free Software Foundation; either version 3 of the License, or
      8  # (at your option) any later version.
      9  #
     10  # Python X2Go is distributed in the hope that it will be useful,
     11  # but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  # GNU Affero General Public License for more details.
     14  #
     15  # You should have received a copy of the GNU Affero General Public License
     16  # along with this program; if not, write to the
     17  # Free Software Foundation, Inc.,
     18  # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
     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  # modules
     31  import os
     32  import copy
     33  import threading
     34  import gevent
     35
     36  # Python X2Go modules
     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
     43
     44 -class X2GoPrintQueue(threading.Thread):
     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
    107 -    def __del__(self):
    108          """\
    109          Class destructor.
    110
    111          """
    112          self.stop_thread()
    113
    114 -    def pause(self):
    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
    124 -    def resume(self):
    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
    134 -    def stop_thread(self):
    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
    144 -    def _incoming_print_jobs(self):
    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
    164 -    def set_print_action(self, print_action, **kwargs):
    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
    185 -    def run(self):
    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      # in case we print a lot we do not want to risk an endlessly growing
    272      # print job history
    273      if len(parent_thread.job_history) > 100:
    274          parent_thread.job_history = parent_thread.job_history[-100:]
    275
    276
    277 -class X2GoPrintJob(threading.Thread):
    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      """
    286 -    def __init__(self, **kwargs):
    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