@Override
  public CauseOfBlockage canRun(Queue.Item item) {
    // Skip locking for multiple configuration projects,
    // only the child jobs will actually lock resources.
    if (item.task instanceof MatrixProject) return null;

    AbstractProject<?, ?> project = Utils.getProject(item);
    if (project == null) return null;

    LockableResourcesStruct resources = Utils.requiredResources(project);
    if (resources == null || (resources.required.isEmpty() && resources.label.isEmpty())) {
      return null;
    }

    int resourceNumber;
    try {
      resourceNumber = Integer.parseInt(resources.requiredNumber);
    } catch (NumberFormatException e) {
      resourceNumber = 0;
    }

    LOGGER.finest(project.getName() + " trying to get resources with these details: " + resources);

    if (resourceNumber > 0 || !resources.label.isEmpty()) {
      Map<String, Object> params = new HashMap<String, Object>();
      if (item.task instanceof MatrixConfiguration) {
        MatrixConfiguration matrix = (MatrixConfiguration) item.task;
        params.putAll(matrix.getCombination());
      }

      List<LockableResource> selected =
          LockableResourcesManager.get()
              .queue(resources, item.id, project.getFullName(), resourceNumber, params, LOGGER);

      if (selected != null) {
        LOGGER.finest(project.getName() + " reserved resources " + selected);
        return null;
      } else {
        LOGGER.finest(project.getName() + " waiting for resources");
        return new BecauseResourcesLocked(resources);
      }

    } else {
      if (LockableResourcesManager.get().queue(resources.required, item.id)) {
        LOGGER.finest(project.getName() + " reserved resources " + resources.required);
        return null;
      } else {
        LOGGER.finest(project.getName() + " waiting for resources " + resources.required);
        return new BecauseResourcesLocked(resources);
      }
    }
  }
    public TestResultAction(String jobs, boolean includeFailedBuilds, AbstractBuild<?, ?> owner) {
      super(owner);
      this.includeFailedBuilds = includeFailedBuilds;

      if (jobs == null) {
        // resolve null as the transitive downstream jobs
        StringBuilder buf = new StringBuilder();
        for (AbstractProject p : getProject().getTransitiveDownstreamProjects()) {
          if (buf.length() > 0) buf.append(',');
          buf.append(p.getFullName());
        }
        jobs = buf.toString();
      }
      this.jobs = jobs;
    }
  private void configureGit(GitHubRepositoryName gitHubRepoName, PullRequestBuildTrigger trigger)
      throws IOException {

    if (project.getScm() instanceof GitSCM) {
      GitSCM git = (GitSCM) project.getScm();
      List<UserRemoteConfig> userRemoteConfigs = new ArrayList<UserRemoteConfig>();

      if (git.getUserRemoteConfigs().isEmpty()
          || (git.getUserRemoteConfigs().size() == 1
              && StringUtils.isBlank(git.getUserRemoteConfigs().get(0).getUrl()))) {

        LOGGER.info(
            MessageFormat.format(
                "Git is selected as SCM of project {0} but not yet configured, configuring it",
                project.getFullName()));

        String url =
            MessageFormat.format(
                "https://{0}/{1}/{2}.git",
                gitHubRepoName.getHost(),
                gitHubRepoName.getUserName(),
                gitHubRepoName.getRepositoryName());

        userRemoteConfigs.add(
            new UserRemoteConfig(url, "origin", "+refs/pull/*:refs/remotes/origin/pr/*", null));

        List<BranchSpec> branches = new ArrayList<BranchSpec>();

        branches.add(new BranchSpec("${PR_COMMIT}"));
        GitSCM updatedGit =
            new GitSCM(
                userRemoteConfigs,
                branches,
                git.isDoGenerateSubmoduleConfigurations(),
                git.getSubmoduleCfg(),
                git.getBrowser(),
                git.getGitTool(),
                git.getExtensions());

        project.setScm(updatedGit);

        LOGGER.info(
            MessageFormat.format(
                "Git is configured to work with {0}", trigger.getDescriptor().getDisplayName()));
      }
    }
  }
 @SuppressWarnings("rawtypes")
 @Override
 public void buildDependencyGraph(AbstractProject owner, DependencyGraph graph) {
   // As DependecyDeclarer was renamed to DependencyDeclarer in Jenkins 1.501,
   // codes referring DependecyDeclarer may not work depending on which version of
   // Jenkins core used.
   // Instead, I use DescribableList#buildDependencyGraph, which is a part of
   // Jenkins core and always work.
   // See JENKINS-25017 for details.
   for (BuildStep publisher : getPublisherList()) {
     if (publisher instanceof Publisher) {
       DescribableList<Publisher, Descriptor<Publisher>> lst =
           new DescribableList<Publisher, Descriptor<Publisher>>(
               new Saveable() {
                 @Override
                 public void save() throws IOException {}
               },
               Arrays.asList((Publisher) publisher));
       lst.buildDependencyGraph(
           owner, new ConditionalDependencyGraphWrapper(graph, condition, runner));
     } else if (publisher instanceof Builder) {
       // Case used with Any Build Step plugin
       // (https://wiki.jenkins-ci.org/display/JENKINS/Any+Build+Step+Plugin).
       DescribableList<Builder, Descriptor<Builder>> lst =
           new DescribableList<Builder, Descriptor<Builder>>(
               new Saveable() {
                 @Override
                 public void save() throws IOException {}
               },
               Arrays.asList((Builder) publisher));
       lst.buildDependencyGraph(
           owner, new ConditionalDependencyGraphWrapper(graph, condition, runner));
     } else if (publisher instanceof DependecyDeclarer) {
       ((DependecyDeclarer) publisher)
           .buildDependencyGraph(
               owner, new ConditionalDependencyGraphWrapper(graph, condition, runner));
     } else {
       LOGGER.log(
           Level.WARNING,
           "May failed to build dependency for {0} in {1}",
           new Object[] {publisher.getClass(), owner.getFullName()});
     }
   }
 }
  private void updateGeneratedJobMap(
      AbstractProject<?, ?> seedJob,
      Set<GeneratedJob> createdOrUpdatedJobs,
      Set<GeneratedJob> removedJobs)
      throws IOException {
    DescriptorImpl descriptor = Jenkins.getInstance().getDescriptorByType(DescriptorImpl.class);
    boolean descriptorMutated = false;
    Map<String, SeedReference> generatedJobMap = descriptor.getGeneratedJobMap();

    for (GeneratedJob generatedJob : createdOrUpdatedJobs) {
      Item item = getLookupStrategy().getItem(seedJob, generatedJob.getJobName(), Item.class);
      if (item != null) {
        SeedReference newSeedReference = new SeedReference(seedJob.getFullName());
        if (generatedJob.getTemplateName() != null) {
          Item template =
              getLookupStrategy().getItem(seedJob, generatedJob.getTemplateName(), Item.class);
          newSeedReference.setTemplateJobName(template.getFullName());
        }
        newSeedReference.setDigest(Util.getDigestOf(Items.getConfigFile(item).getFile()));

        SeedReference oldSeedReference = generatedJobMap.get(item.getFullName());
        if (!newSeedReference.equals(oldSeedReference)) {
          generatedJobMap.put(item.getFullName(), newSeedReference);
          descriptorMutated = true;
        }
      }
    }

    for (GeneratedJob removedJob : removedJobs) {
      Item removedItem = getLookupStrategy().getItem(seedJob, removedJob.getJobName(), Item.class);
      if (removedItem != null) {
        generatedJobMap.remove(removedItem.getFullName());
        descriptorMutated = true;
      }
    }

    if (descriptorMutated) {
      descriptor.save();
    }
  }
  public PullRequestBuildHandler(AbstractProject<?, ?> project, boolean newTrigger)
      throws IOException {
    this.project = project;

    final GithubProjectProperty property = project.getProperty(GithubProjectProperty.class);

    if (property == null
        || property.getProjectUrl() == null
        || StringUtils.isBlank(property.getProjectUrl().baseUrl())) {

      throw new IOException(
          MessageFormat.format(
              "GitHub project URL must be specified for project {0}", project.getFullName()));
    }

    String gitHubProjectUrl = property.getProjectUrl().baseUrl().trim();
    GitHubRepositoryName gitHubRepoName = GitHubRepositoryName.create(gitHubProjectUrl);
    if (gitHubRepoName == null) {
      throw new IOException(
          MessageFormat.format("Invalid GitHub project URL specified: {0}", gitHubProjectUrl));
    }

    GHRepository repo = gitHubRepoName.resolveOne();
    if (repo == null) {
      LOGGER.severe(
          MessageFormat.format(
              "Cannot connect to {0}. Please check your registered GitHub credentials",
              gitHubRepoName));

      gitHubRepositoryUrl = gitHubProjectUrl;
    } else {
      gitHubRepositoryUrl = repo.getHtmlUrl().toString();
    }
    if (!gitHubRepositoryUrl.endsWith("/")) {
      gitHubRepositoryUrl += '/';
    }

    PullRequestBuildTrigger trigger = project.getTrigger(PullRequestBuildTrigger.class);
    triggerPhrasePattern = Pattern.compile(trigger.getTriggerPhrase(), Pattern.CASE_INSENSITIVE);
    if (StringUtils.isNotBlank(trigger.getWhitelist())) {
      Set<String> entries = new HashSet<String>();
      for (String entry : trigger.getWhitelist().split(",")) {
        if (StringUtils.isNotBlank(entry)) {
          entries.add(entry);
        }
      }
      if (!entries.isEmpty()) {
        whitelist = entries;
      }
    }

    if (newTrigger) {
      configureGit(gitHubRepoName, trigger);

      // The construction of webhook URL requires Jenkins root URL which might be null if it is not
      // manually
      // configured and it is retrieved in a separate thread without request context. So the webhook
      // URL is first
      // retrieved here.
      PullRequestBuildTrigger.DescriptorImpl descriptor =
          (PullRequestBuildTrigger.DescriptorImpl)
              Jenkins.getInstance().getDescriptor(PullRequestBuildTrigger.class);

      final String webhookUrl =
          StringUtils.isBlank(descriptor.getWebHookExternalUrl())
              ? descriptor.getWebHookUrl()
              : descriptor.getWebHookExternalUrl();

      if (webhookUrl == null) {
        LOGGER.warning(
            MessageFormat.format(
                "Cannot add webhook to GitHub repository {0}. "
                    + "Please configure Jenkins URL or Webhook External URL for {1}",
                gitHubRepositoryUrl, descriptor.getDisplayName()));

      } else {
        LOGGER.info(
            MessageFormat.format(
                "Adding webhook {0} to GitHub repository {1}", webhookUrl, gitHubRepositoryUrl));

        sequentialExecutionQueue.execute(
            new Runnable() {

              public void run() {
                try {
                  createWebHook(webhookUrl);
                } catch (Throwable ex) {
                  LOGGER.log(
                      Level.SEVERE,
                      MessageFormat.format(
                          "Error adding webhook to GitHub repository {0}", gitHubRepositoryUrl),
                      ex);
                }
              }
            });
      }
    }
  }
  void handle(GHEventPayload.IssueComment issueComment, GitHub gitHub) throws IOException {
    // check the trigger phrase
    PullRequestBuildTrigger trigger = project.getTrigger(PullRequestBuildTrigger.class);
    if (StringUtils.isBlank(trigger.getTriggerPhrase())) {
      if (LOGGER.isLoggable(Level.FINE)) {
        LOGGER.fine("No trigger phrase configured for project: " + project.getDisplayName());
      }
      return;
    }

    String issueUrl = issueComment.getIssue().getHtmlUrl().toString();
    if (!issueUrl.startsWith(gitHubRepositoryUrl)) {
      LOGGER.finest(
          MessageFormat.format(
              "GitHub issue {0} is not related to project {1}. "
                  + "GitHub project URL configured for project {1}: {2}",
              issueUrl, project.getFullName(), gitHubRepositoryUrl));

      return;
    }

    String commentBody = issueComment.getComment().getBody();
    if (!triggerPhrasePattern.matcher(commentBody).find()) {
      if (LOGGER.isLoggable(Level.FINE)) {
        LOGGER.fine("No trigger phrase matching on comment: " + commentBody);
      }
      return;
    }

    GHUser buildRequester = issueComment.getComment().getUser();
    if (!isWhitelisted(buildRequester, gitHub)) {
      LOGGER.info(
          MessageFormat.format(
              "GitHub user {0} is not in the whitelist of project {1}",
              buildRequester.getLogin(), project.getFullName()));

      return;
    }

    final int prNumber = issueComment.getIssue().getNumber();

    GHPullRequest pullRequest = issueComment.getRepository().getPullRequest(prNumber);
    // Updating PR to force the cache (if present) to refresh:
    pullRequest.setTitle(pullRequest.getTitle());

    pullRequest = issueComment.getRepository().getPullRequest(prNumber);

    if (LOGGER.isLoggable(Level.FINE)) {
      LOGGER.fine("ghPullRequest = " + getPullRequestAsString(pullRequest));
    }

    if (pullRequest.getState() == GHIssueState.OPEN) {
      PullRequestData pullRequestData =
          PullRequestManager.getInstance().addPullRequestData(pullRequest, project);

      cancelBuilds(pullRequestData);
      build(pullRequest, buildRequester, new TriggerCause(pullRequest, buildRequester));
    } else if (LOGGER.isLoggable(Level.FINE)) {
      LOGGER.fine(
          MessageFormat.format(
              "Pull request {0} is not opened, no build is triggered",
              pullRequest.getHtmlUrl().toString()));
    }
  }
  void handle(GHEventPayload.PullRequest prEventPayload, GitHub gitHub) throws IOException {
    GHPullRequest pullRequest = prEventPayload.getPullRequest();
    String pullRequestUrl = pullRequest.getHtmlUrl().toString();
    if (!pullRequestUrl.startsWith(gitHubRepositoryUrl)) {
      LOGGER.config(
          MessageFormat.format(
              "Pull request {0} is not related to project {1}. "
                  + "GitHub project URL configured for project {1}: {2}",
              pullRequestUrl, project.getFullName(), gitHubRepositoryUrl));
      return;
    }

    LOGGER.info(
        MessageFormat.format(
            "Handling event ''{0}'' of pull request {1} for project {2}",
            prEventPayload.getAction(), pullRequestUrl, project.getFullName()));

    PullRequestManager pullRequestManager = PullRequestManager.getInstance();
    PullRequestData pullRequestData =
        pullRequestManager.getPullRequestData(pullRequestUrl, project);

    if (PullRequestManager.PullRequestAction.CLOSED.equals(prEventPayload.getAction())) {
      if (pullRequestData != null) {
        cancelBuilds(pullRequestData);
        deleteInstances(pullRequest);
      } else {
        LOGGER.warning(
            "No previous data available for received Pull Request 'close' event: "
                + pullRequestUrl);
      }
      return;
    }

    if (!isWhitelisted(pullRequest.getUser(), gitHub)) {
      LOGGER.info(
          MessageFormat.format(
              "GitHub user {0} is not in the whitelist of project {1}",
              pullRequest.getUser().getLogin(), project.getFullName()));

      return;
    }

    boolean startBuild = false;

    if (pullRequestData == null) {
      if (PullRequestManager.PullRequestAction.SYNCHRONIZE.equals(prEventPayload.getAction())) {
        LOGGER.info(
            MessageFormat.format(
                "Updated pull request {0} was not built previously", pullRequestUrl));
      }
      pullRequestData = pullRequestManager.addPullRequestData(pullRequest, project);
      startBuild = pullRequestData.getLastUpdated().equals(pullRequest.getUpdatedAt());

    } else if (pullRequestData.update(pullRequest)) {
      pullRequestData.save();
      startBuild = true;
    }

    if (LOGGER.isLoggable(Level.FINEST)) {
      LOGGER.finest("Received event payload: " + prEventPayload);
    }

    if (startBuild) {
      if (LOGGER.isLoggable(Level.FINE)) {
        LOGGER.fine(
            "Cancelling previous running builds and starting new build for Pull request: "
                + pullRequestData);
      }
      cancelBuilds(pullRequestData);
      build(pullRequest, null, new TriggerCause(prEventPayload));

    } else if (LOGGER.isLoggable(Level.FINE)) {
      LOGGER.fine("No new build has been triggered for Pull request: " + pullRequestData);
    }
  }