code: @Override
    public synchronized Iterator<JobSpec> getJobSpecIterator() {
      List<Path> jobFiles = loader.fetchJobFilesRecursively(loader.getRootDirectory());
      Iterator<JobSpec> jobSpecIterator = Iterators.transform(jobFiles.iterator(), new Function<Path, JobSpec>() {
        @Nullable
        @Override
        public JobSpec apply(@Nullable Path jobFile) {
          if (jobFile == null) {
            return null;
          }
  
          try {
            Config config = ImmutableFSJobCatalog.this.loader.loadPullFile(jobFile,
              ImmutableFSJobCatalog.this.sysConfig, ImmutableFSJobCatalog.this.shouldLoadGlobalConf());
  
            return ImmutableFSJobCatalog.this.converter.apply(config);
          } catch (IOException e) {
            log.error("Cannot load job from {} due to {}", jobFile, ExceptionUtils.getFullStackTrace(e));
            return null;
          }
        }
      });
  
      return jobSpecIterator;
    }
docs: Return an iterator that can fetch all the job specifications.
  Different than {@link #getJobs()}, this method prevents loading
  all the job configs into memory in the very beginning. Instead it
  only loads file paths initially and creates the JobSpec and the
  underlying {@link Config} during the iteration.
  
  @return an iterator that contains job specs (job specs can be null).