/** {@inheritDoc} */
 @Override
 public UpdateScmResult executeUpdateCommand(
     ScmProviderRepository repository, ScmFileSet fileSet, ScmVersion scmVersion)
     throws ScmException {
   getLogger()
       .info("Attempting to synchronize sandbox in " + fileSet.getBasedir().getAbsolutePath());
   List<ScmFile> updatedFiles = new ArrayList<ScmFile>();
   IntegrityScmProviderRepository iRepo = (IntegrityScmProviderRepository) repository;
   Sandbox siSandbox = iRepo.getSandbox();
   try {
     // Make sure we've got a valid sandbox, otherwise create it...
     if (siSandbox.create()) {
       Response res = siSandbox.resync();
       // Lets capture what we got from running this resync
       WorkItemIterator wit = res.getWorkItems();
       while (wit.hasNext()) {
         WorkItem wi = wit.next();
         if (wi.getModelType().equals(SIModelTypeName.MEMBER)) {
           Result message = wi.getResult();
           getLogger()
               .debug(wi.getDisplayId() + " " + (null != message ? message.getMessage() : ""));
           if (null != message && message.getMessage().length() > 0) {
             updatedFiles.add(
                 new ScmFile(
                     wi.getDisplayId(),
                     message.getMessage().equalsIgnoreCase("removed")
                         ? ScmFileStatus.DELETED
                         : ScmFileStatus.UPDATED));
           }
         }
       }
       return new UpdateScmResult(res.getCommandString(), updatedFiles);
     } else {
       return new UpdateScmResult("si resync", "Failed to synchronize workspace", "", false);
     }
   } catch (APIException aex) {
     ExceptionHandler eh = new ExceptionHandler(aex);
     getLogger().error("MKS API Exception: " + eh.getMessage());
     getLogger().info(eh.getCommand() + " exited with return code " + eh.getExitCode());
     return new UpdateScmResult(
         eh.getCommand(), eh.getMessage(), "Exit Code: " + eh.getExitCode(), false);
   }
 }
  /**
   * Parses the output from the si viewproject command to get a list of members and updates Derby DB
   *
   * @param workItems WorkItemIterator
   * @throws APIException
   * @throws SQLException
   * @throws InterruptedException
   * @throws ExecutionException
   */
  public void parseProject(WorkItemIterator workItems)
      throws APIException, SQLException, InterruptedException, ExecutionException {

    ExecutorService executor = null;
    Map<String, String> pjConfigHash = new Hashtable<String, String>();
    List<Future<Void>> futures = new ArrayList<Future<Void>>();

    // Setup the Derby DB for this Project
    // Create a fresh set of tables for this project
    DerbyUtils.createCMProjectTables(
        DescriptorImpl.INTEGRITY_DESCRIPTOR.getDataSource(), this.getProjectCacheTable());

    LOGGER.log(Level.INFO, "Starting Parse tasks for Derby DB");

    final ThreadFactory threadFactory =
        new ThreadFactoryBuilder().setNameFormat("Parse-Derby-Project-Task-%d").build();
    // Initialize executor for folder path processing
    executor = Executors.newFixedThreadPool(10, threadFactory);

    pjConfigHash.put(this.getProjectName(), this.getConfigurationPath());

    while (workItems.hasNext()) {
      WorkItem wi = workItems.next();

      if (wi.getModelType().equals(SIModelTypeName.SI_SUBPROJECT)) {
        // Parse folders separately from members in an asynchronous environment. This is to be
        // executed before member parsing!
        LOGGER.log(
            Level.FINE, "Executing parse folder task :" + wi.getField("name").getValueAsString());
        Map<String, String> future = executor.submit(new ParseProjectFolderTask(wi, this)).get();
        for (String key : future.keySet()) {
          LOGGER.log(
              Level.FINE,
              "Adding folder key in project configuration. Key: "
                  + key
                  + ", Value: "
                  + future.get(key));
          pjConfigHash.put(key, future.get(key));
        }
      } else if (wi.getModelType().equals(SIModelTypeName.MEMBER)) {
        // Parse member tasks
        LOGGER.log(
            Level.FINE, "Executing parse member task :" + wi.getField("name").getValueAsString());
        futures.add(executor.submit(new ParseProjectMemberTask(wi, pjConfigHash, this)));
      } else {
        LOGGER.log(
            Level.WARNING,
            "View project output contains an invalid model type: " + wi.getModelType());
      }
    }

    for (Future<Void> f : futures) {
      // Wait for all threads to finish
      f.get();
    }

    LOGGER.log(Level.INFO, "Parsing project " + this.getConfigurationPath() + " complete!");

    if (null != executor) {
      executor.shutdown();
      executor.awaitTermination(2, TimeUnit.MINUTES);
      LOGGER.log(Level.FINE, "Parse Project Executor shutdown.");
    }
  }