def clean(self, jobCache=None):
        # Iterate from the root jobWrapper and collate all jobs that are reachable from it
        # All other jobs returned by self.jobs() are orphaned and can be removed
        reachableFromRoot = set()

        if jobCache is None:
            logger.warning("Cleaning jobStore recursively. This may be slow.")

        def getJob(jobId):
            if jobCache is not None:
                return jobCache[jobId]
            else:
                return self.load(jobId)

        def haveJob(jobId):
            if jobCache is not None:
                return jobCache.has_key(jobId)
            else:
                return self.exists(jobId)

        def getJobs():
            if jobCache is not None:
                return jobCache.itervalues()
            else:
                return self.jobs()

        def getConnectedJobs(jobWrapper):
            if jobWrapper.jobStoreID in reachableFromRoot:
                return
            reachableFromRoot.add(jobWrapper.jobStoreID)
            # Traverse jobs in stack
            for jobs in jobWrapper.stack:
                for successorJobStoreID in map(lambda x: x[0], jobs):
                    if successorJobStoreID not in reachableFromRoot and haveJob(successorJobStoreID):
                        getConnectedJobs(getJob(successorJobStoreID))
            # Traverse service jobs
            for jobs in jobWrapper.services:
                for serviceJobStoreID in map(lambda x: x[0], jobs):
                    assert serviceJobStoreID not in reachableFromRoot
                    reachableFromRoot.add(serviceJobStoreID)

        logger.info("Checking job graph connectivity...")
        getConnectedJobs(self.loadRootJob())
        logger.info("%d jobs reachable from root." % len(reachableFromRoot))

        # Cleanup the state of each jobWrapper
        for jobWrapper in getJobs():
            changed = [False]  # Flag to indicate if we need to update the jobWrapper
            # on disk

            if len(jobWrapper.filesToDelete) != 0:
                # Delete any files that should already be deleted
                for fileID in jobWrapper.filesToDelete:
                    logger.critical(
                        "Removing file in job store: %s that was marked for deletion but not previously removed" % fileID)
                    self.deleteFile(fileID)
                jobWrapper.filesToDelete = []
                changed[0] = True

            # Delete a jobWrapper if it is not reachable from the rootJob
            if jobWrapper.jobStoreID not in reachableFromRoot:
                logger.critical(
                    "Removing job: %s that is not a successor of the root job in cleanup" % jobWrapper.jobStoreID)
                self.delete(jobWrapper.jobStoreID)
                continue

            # For a job whose command is already execute, remove jobs from the 
            # stack that are already deleted. 
            # This cleans up the case that the jobWrapper
            # had successors to run, but had not been updated to reflect this
            
            if jobWrapper.command is None:
                stackSize = sum(map(len, jobWrapper.stack))
                # Remove deleted jobs
                jobWrapper.stack = map(lambda x : filter(lambda y : self.exists(y[0]), x), jobWrapper.stack)
                # Remove empty stuff from the stack
                jobWrapper.stack = filter(lambda x : len(x) > 0, jobWrapper.stack)
                # Check if anything go removed
                if sum(map(len, jobWrapper.stack)) != stackSize:
                    changed[0] = True
                
            # Cleanup any services that have already been finished
            # Filter out deleted services and update the flags for services that exist
            # If there are services then renew  
            # the start and terminate flags if they have been removed
            def subFlagFile(jobStoreID, jobStoreFileID, flag):
                if self.fileExists(jobStoreFileID):
                    return jobStoreFileID
                
                # Make a new flag
                newFlag = self.getEmptyFileStoreID()
                
                # Load the jobWrapper for the service and initialise the link
                serviceJobWrapper = getJob(jobStoreID)
                
                if flag == 1:
                    logger.debug("Recreating a start service flag for job: %s, flag: %s", jobStoreID, newFlag)
                    serviceJobWrapper.startJobStoreID = newFlag
                elif flag == 2:
                    logger.debug("Recreating a terminate service flag for job: %s, flag: %s", jobStoreID, newFlag)
                    serviceJobWrapper.terminateJobStoreID = newFlag
                else:
                    logger.debug("Recreating a error service flag for job: %s, flag: %s", jobStoreID, newFlag)
                    assert flag == 3
                    serviceJobWrapper.errorJobStoreID = newFlag
                    
                # Update the service job on disk
                self.update(serviceJobWrapper)
                
                changed[0] = True
                
                return newFlag
            
            servicesSize = sum(map(len, jobWrapper.services))
            jobWrapper.services = filter(lambda z : len(z) > 0, map(lambda serviceJobList : 
                                        map(lambda x : x[:4] + (subFlagFile(x[0], x[4], 1), 
                                                                subFlagFile(x[0], x[5], 2), 
                                                                subFlagFile(x[0], x[6], 3)), 
                                        filter(lambda y : self.exists(y[0]), serviceJobList)), jobWrapper.services)) 
            if sum(map(len, jobWrapper.services)) != servicesSize:
                changed[0] = True

            # Reset the retry count of the jobWrapper
            if jobWrapper.remainingRetryCount != self._defaultTryCount():
                jobWrapper.remainingRetryCount = self._defaultTryCount()
                changed[0] = True

            # This cleans the old log file which may
            # have been left if the jobWrapper is being retried after a jobWrapper failure.
            if jobWrapper.logJobStoreFileID != None:
                self.delete(jobWrapper.logJobStoreFileID)
                jobWrapper.logJobStoreFileID = None
                changed[0] = True

            if changed[0]:  # Update, but only if a change has occurred
                logger.critical("Repairing job: %s" % jobWrapper.jobStoreID)
                self.update(jobWrapper)

        # Remove any crufty stats/logging files from the previous run
        logger.info("Discarding old statistics and logs...")
        self.readStatsAndLogging(lambda x: None)

        logger.info("Job store is clean")
        # TODO: reloading of the rootJob may be redundant here
        return self.loadRootJob()

    ##########################################
    # The following methods deal with creating/loading/updating/writing/checking for the
    # existence of jobs
    ##########################################
Adds a new supported extension. This is useful for servers to
dataset/ETHPy150Open BD2KGenomics/toil/src/toil/jobStores/abstractJobStore.py/AbstractJobStore.clean--crossover with--dataset/ETHPy150Open slimta/python-slimta/slimta/smtp/extensions.py/Extensions.add