Index: server/lib/gutenbach/server/job.py
===================================================================
--- server/lib/gutenbach/server/job.py	(revision 33ea5054308a544bb5f939b99ad15f59fa4b6d7a)
+++ server/lib/gutenbach/server/job.py	(revision 345c4768c17a73ba2749c01a590747645f06393f)
@@ -10,5 +10,6 @@
 class GutenbachJob(object):
 
-    def __init__(self, job_id=None, creator=None, name=None):
+    def __init__(self, job_id=None, creator=None, name=None,
+                 priority=None, document=None):
 	"""Create an empty Gutenbach job.
 
@@ -16,11 +17,14 @@
 
         self.player = None
+        self.document = None
 
 	self.id = job_id
         self.creator = creator
         self.name = name
-	self.state = States.HELD
-        self.priority = 1
-        self.document = None
+        self.priority = priority
+        self._why_done = None
+
+        if document is not None:
+            self.spool(document)
 
     def __repr__(self):
@@ -47,7 +51,17 @@
     def id(self, val):
         try:
-            self._id = int(val)
+            self._id = max(int(val), -1)
         except TypeError:
             self._id = -1
+
+    @property
+    def priority(self):
+        return self._priority
+    @priority.setter
+    def priority(self, val):
+        try:
+            self._priority = max(int(val), 1)
+        except TypeError:
+            self._priority = 1
 
     @property
@@ -90,32 +104,79 @@
 
     @property
+    def is_valid(self):
+        """Whether the job is ready to be manipulated (spooled,
+        played, etc).  Note that playing the job still requires it to
+        be spooled first.
+
+        """
+        return self.id > 0 and \
+               self.priority > 0
+
+    @property
+    def is_ready(self):
+        """Whether the job is ready to be played.
+
+        """
+        return self.is_valid and \
+               self.player is not None and \
+               not self.player.is_playing and \
+               not self._why_done == "cancelled" and \
+               not self._why_done == "aborted"
+
+    @property
     def is_playing(self):
-        return self.state == States.PROCESSING
-
-    @property
-    def is_ready(self):
-        return self.state == States.PENDING
-
-    @property
-    def is_finished(self):
-        return self.state != States.PENDING and \
-               self.state != States.PROCESSING and \
-               self.state != States.HELD
-        
+        """Whether the job is currently playing (regardless of whether
+        it's paused).
+
+        """
+        return self.is_valid and \
+               self.player is not None and \
+               self.player.is_playing
+
+    @property
+    def is_paused(self):
+        """Whether the job is currently paused.
+
+        """
+        return self.is_valid and \
+               self.player is not None and \
+               self.player.is_paused        
+
+    @property
+    def is_done(self):
+        return (self.is_valid and \
+                self.player is not None and \
+                self.player.is_done) or \
+                (self._why_done == "cancelled" or \
+                 self._why_done == "aborted")
+
+    @property
+    def state(self):
+        if self.is_ready:
+            state = States.PENDING
+        elif self.is_playing and not self.is_paused:
+            state = States.PROCESSING
+        elif self.is_playing and self.is_paused:
+            state = States.STOPPED
+        elif self.is_done and self._why_done == "completed":
+            state = States.COMPLETE
+        elif self.is_done and self._why_done == "cancelled":
+            state = States.CANCELLED
+        elif self.is_done and self._why_done == "aborted":
+            state = States.ABORTED
+        else:
+            state = States.HELD
+        return state
+
     ######################################################################
     ###                            Methods                             ###
     ######################################################################
 
-    def spool(self, document, username=None):
-        if self.state != States.HELD:
-            raise InvalidJobStateException(self.state)
-
+    def spool(self, document):
+        if not self.is_valid:
+            raise InvalidJobStateException(self.state)
         self.document = document.name
         self.player = Player(document)
-        self.creator = username
-        self.state = States.PENDING
-
         logger.debug("document for job %d is '%s'" % (self.id, self.document))
-
 
     def play(self):
@@ -140,8 +201,5 @@
         def _completed():
             logger.info("completed job %s" % str(self))
-            self.state = States.COMPLETE
-            self.player = None
-
-	self.state = States.PROCESSING
+            self._why_done = "completed"
         self.player.callback = _completed
         self.player.start()
@@ -155,35 +213,31 @@
         if not self.is_playing:
             raise InvalidJobStateException(self.state)
-        
         self.player.mplayer_pause()
-        self.state = States.STOPPED
 
     def cancel(self):
-        def _canceled():
-            logger.info("canceled job %s" % str(self))
-            self.state = States.CANCELLED
-            self.player = None
+        def _cancelled():
+            logger.info("cancelled job %s" % str(self))
+            self._why_done = "cancelled"
 
         if self.is_playing:
-            self.player.callback = _canceled
+            self.player.callback = _cancelled
             self.player.mplayer_stop()
-        elif self.is_finished:
-            raise InvalidJobStateException(self.state)
-        
-        self.state = States.CANCELLED
+        elif self.is_done and not self._why_done == "cancelled":
+            raise InvalidJobStateException(self.state)
+        else:
+            _cancelled()
 
     def abort(self):
         def _aborted():
             logger.info("aborted job %s" % str(self))
-            self.state = States.ABORTED
-            self.player = None
+            self._why_done = "aborted"
 
         if self.is_playing:
             self.player.callback = _aborted
             self.player.mplayer_stop()
-        elif self.is_finished:
-            raise InvalidJobStateException(self.state)
-        
-        self.state = States.ABORTED
-
-
+        elif self.is_done and not self._why_done == "aborted":
+            raise InvalidJobStateException(self.state)
+        else:
+            _aborted()
+
+
Index: server/lib/gutenbach/server/player.py
===================================================================
--- server/lib/gutenbach/server/player.py	(revision 33ea5054308a544bb5f939b99ad15f59fa4b6d7a)
+++ server/lib/gutenbach/server/player.py	(revision 345c4768c17a73ba2749c01a590747645f06393f)
@@ -17,4 +17,5 @@
             self.player = None
             self._callback = None
+            self._paused = False
 
     @property
@@ -23,4 +24,16 @@
             playing = self.player and self.player.poll() is None
         return playing
+
+    @property
+    def is_paused(self):
+        with self.lock:
+            paused = self.is_playing and self._paused
+        return paused
+
+    @property
+    def is_done(self):
+        with self.lock:
+            done = self.player and self.player.poll() is not None
+        return done
 
     @property
@@ -41,9 +54,10 @@
                 self.callback()
 
-        self.fh.close()
-        self.fh = None
-
     def mplayer_play(self):
+        if not self.isAlive():
+            return
+        
         logger.info("playing file '%s'" % self.fh.name)
+        self._paused = False
         
         # open mplayer
@@ -63,5 +77,5 @@
                 break
             time.sleep(0.1)
-            
+
         logger.info("mplayer finished with code %d" % self.player.returncode)
 
@@ -77,9 +91,16 @@
 
     def mplayer_pause(self):
+        if not self.isAlive():
+            return
+        
         with self.lock:
             if self.is_playing:
                 self.player.stdin.write("pause\n")
+                self._paused = not(self._paused)
                 
     def mplayer_stop(self):
+        if not self.isAlive():
+            return
+        
         with self.lock:
             if self.is_playing:
Index: server/lib/gutenbach/server/printer.py
===================================================================
--- server/lib/gutenbach/server/printer.py	(revision 33ea5054308a544bb5f939b99ad15f59fa4b6d7a)
+++ server/lib/gutenbach/server/printer.py	(revision 345c4768c17a73ba2749c01a590747645f06393f)
@@ -101,5 +101,5 @@
                     if self.current_job is None:
                         self.start_job()
-                    elif self.current_job.is_finished:
+                    elif self.current_job.is_done:
                         self.complete_job()
                 except:
@@ -164,5 +164,5 @@
 
             try:
-                if not self.current_job.is_finished:
+                if not self.current_job.is_done:
                     self.current_job.stop()
             finally:
@@ -382,5 +382,5 @@
 
         job = self.get_job(job_id)
-        job.spool(document, username=requesting_user_name)
+        job.spool(document)
 
     def send_uri(self):
Index: server/test/server/job.py
===================================================================
--- server/test/server/job.py	(revision 09790fe1de7a28f29c27ef1c74004aff3c74b66d)
+++ server/test/server/job.py	(revision 345c4768c17a73ba2749c01a590747645f06393f)
@@ -32,10 +32,17 @@
 
     def testStateProperties(self):
+        self.assertFalse(self.job.is_valid)
+        self.assertFalse(self.job.is_ready)
         self.assertFalse(self.job.is_playing)
-        self.assertFalse(self.job.is_ready)
-        self.assertFalse(self.job.is_finished)
+        self.assertFalse(self.job.is_paused)
+        self.assertFalse(self.job.is_done)
 
     def testSpool(self):
         fh = make_tempfile()
+        # This should fail, because the id hasn't been set
+        self.assertFalse(self.job.is_valid)
+        self.job.id = 1
+        self.assertTrue(self.job.is_valid)
+        self.assertFalse(self.job.is_ready)
         self.job.spool(fh)
         self.assertEqual(self.job.document, fh.name)
@@ -43,7 +50,4 @@
         self.assertEqual(self.job.creator, "")
         self.assertEqual(self.job.state, States.PENDING)
-        # This should fail, because the id hasn't been set
-        self.assertFalse(self.job.is_ready)
-        self.job.id = 1
         self.assertTrue(self.job.is_ready)
         self.job.abort()
