Package x2go :: Module printqueue

[frames] | no frames]

# Source Code for Module x2go.printqueue

      1  # -*- coding: utf-8 -*-
      2
      3  # Copyright (C) 2010-2013 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  # we hide the default values from epydoc (that's why we transform them to _UNDERSCORE variables)
     42  from backends.printing import X2GoClientPrinting as _X2GoClientPrinting
     43
     44  from defaults import X2GO_PRINTING_FILENAME as _X2GO_PRINTING_FILENAME
     45
     46
     47 -class X2GoPrintQueue(threading.Thread):
     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
    110 -    def __del__(self):
    111          """\
    112          Class destructor.
    113
    114          """
    115          self.stop_thread()
    116
    117 -    def pause(self):
    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
    127 -    def resume(self):
    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
    137 -    def stop_thread(self):
    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
    147 -    def _incoming_print_jobs(self):
    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
    167 -    def set_print_action(self, print_action, **kwargs):
    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
    188 -    def run(self):
    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      # in case we print a lot we do not want to risk an endlessly growing
    275      # print job history
    276      if len(parent_thread.job_history) > 100:
    277          parent_thread.job_history = parent_thread.job_history[-100:]
    278
    279
    280 -class X2GoPrintJob(threading.Thread):
    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      """
    289 -    def __init__(self, **kwargs):
    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