Skip to content

Commit

Permalink
Merge tag 'cloudbees-bitbucket-branch-source-2.2.12' into fixes
Browse files Browse the repository at this point in the history
[maven-release-plugin] copy for tag cloudbees-bitbucket-branch-source-2.2.12
  • Loading branch information
benjaminfuchs committed Jul 15, 2018
2 parents 18f90a0 + 811e7ff commit d79fcee
Show file tree
Hide file tree
Showing 13 changed files with 241 additions and 54 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
</parent>

<artifactId>cloudbees-bitbucket-branch-source</artifactId>
<version>2.2.11-fixes-4e67e3b</version>
<version>2.2.12-fixes-4e67e3b</version>
<packaging>hpi</packaging>

<name>Bitbucket Branch Source Plugin</name>
Expand Down Expand Up @@ -74,7 +74,7 @@
<connection>scm:git:git://github.com/jenkinsci/bitbucket-branch-source-plugin.git</connection>
<developerConnection>scm:git:[email protected]:jenkinsci/bitbucket-branch-source-plugin.git</developerConnection>
<url>https://github.com/jenkinsci/bitbucket-branch-source-plugin</url>
<tag>cloudbees-bitbucket-branch-source-2.2.11</tag>
<tag>cloudbees-bitbucket-branch-source-2.2.12</tag>
</scm>

<dependencyManagement>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,16 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.model.Jenkins;
import jenkins.scm.api.SCMFile;

import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.NANOSECONDS;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpHost;
Expand Down Expand Up @@ -122,24 +126,36 @@ public class BitbucketCloudApiClient implements BitbucketApi {
private final String owner;
private final String repositoryName;
private final UsernamePasswordCredentials credentials;
private final boolean enableCache;
static {
connectionManager.setDefaultMaxPerRoute(20);
connectionManager.setMaxTotal(22);
connectionManager.setSocketConfig(API_HOST, SocketConfig.custom().setSoTimeout(60 * 1000).build());
}
private static Cache<String, BitbucketTeam> cachedTeam = new Cache(6, TimeUnit.HOURS);
private static Cache<String, List<BitbucketCloudRepository>> cachedRepositories = new Cache(3, TimeUnit.HOURS);
private static Cache<String, BitbucketTeam> cachedTeam = new Cache(6, HOURS);
private static Cache<String, List<BitbucketCloudRepository>> cachedRepositories = new Cache(3, HOURS);
private transient BitbucketRepository cachedRepository;
private transient String cachedDefaultBranch;

public BitbucketCloudApiClient(String owner, String repositoryName, StandardUsernamePasswordCredentials creds) {
public static void clearCaches() {
cachedTeam.evictAll();
cachedRepositories.evictAll();
}

public BitbucketCloudApiClient(boolean enableCache, int teamCacheDuration, int repositoriesCacheDuraction,
String owner, String repositoryName, StandardUsernamePasswordCredentials creds) {
if (creds != null) {
this.credentials = new UsernamePasswordCredentials(creds.getUsername(), Secret.toString(creds.getPassword()));
} else {
this.credentials = null;
}
this.owner = owner;
this.repositoryName = repositoryName;
this.enableCache = enableCache;
if (enableCache) {
cachedTeam.setExpireDuration(teamCacheDuration, MINUTES);
cachedRepositories.setExpireDuration(repositoriesCacheDuraction, MINUTES);
}

// Create Http client
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
Expand Down Expand Up @@ -298,7 +314,7 @@ public BitbucketRepository getRepository() throws IOException, InterruptedExcept
if (repositoryName == null) {
throw new UnsupportedOperationException("Cannot get a repository from an API instance that is not associated with a repository");
}
if (cachedRepository == null) {
if (!enableCache || cachedRepository == null) {
String url = UriTemplate.fromTemplate(REPO_URL_TEMPLATE)
.set("owner", owner)
.set("repo", repositoryName)
Expand Down Expand Up @@ -354,7 +370,7 @@ public boolean checkPathExists(@NonNull String branchOrHash, @NonNull String pat
@CheckForNull
@Override
public String getDefaultBranch() throws IOException, InterruptedException {
if (cachedDefaultBranch == null) {
if (!enableCache || cachedDefaultBranch == null) {
String url = UriTemplate.fromTemplate(REPO_URL_TEMPLATE + "/{?fields}")
.set("owner", owner)
.set("repo", repositoryName)
Expand Down Expand Up @@ -565,21 +581,28 @@ public BitbucketTeam getTeam() throws IOException, InterruptedException {
final String url = UriTemplate.fromTemplate(V2_TEAMS_API_BASE_URL + "{/owner}")
.set("owner", owner)
.expand();
try {
return cachedTeam.get(owner, new Callable<BitbucketTeam>() {
@Override
public BitbucketTeam call() throws Exception {
try {
String response = getRequest(url);
return JsonParser.toJava(response, BitbucketCloudTeam.class);
} catch (FileNotFoundException e) {
return null;
} catch (IOException e) {
throw new IOException("I/O error when parsing response from URL: " + url, e);
}

Callable<BitbucketTeam> request = new Callable<BitbucketTeam>() {
@Override
public BitbucketTeam call() throws Exception {
try {
String response = getRequest(url);
return JsonParser.toJava(response, BitbucketCloudTeam.class);
} catch (FileNotFoundException e) {
return null;
} catch (IOException e) {
throw new IOException("I/O error when parsing response from URL: " + url, e);
}
});
} catch (ExecutionException ex) {
}
};

try {
if (enableCache) {
return cachedTeam.get(owner, request);
} else {
return request.call();
}
} catch (Exception ex) {
return null;
}
}
Expand All @@ -601,34 +624,39 @@ public List<BitbucketCloudRepository> getRepositories(@CheckForNull UserRoleInRe
template.set("role", role.getId());
cacheKey.append("::").append(role.getId());
}
Callable<List<BitbucketCloudRepository>> request = new Callable<List<BitbucketCloudRepository>>() {
@Override
public List<BitbucketCloudRepository> call() throws Exception {
List<BitbucketCloudRepository> repositories = new ArrayList<BitbucketCloudRepository>();
Integer pageNumber = 1;
String url, response;
PaginatedBitbucketRepository page;
do {
response = getRequest(url = template.set("page", pageNumber).expand());
try {
page = JsonParser.toJava(response, PaginatedBitbucketRepository.class);
repositories.addAll(page.getValues());
} catch (IOException e) {
throw new IOException("I/O error when parsing response from URL: " + url, e);
}
pageNumber++;
} while (page.getNext() != null);
Collections.sort(repositories, new Comparator<BitbucketCloudRepository>() {
@Override
public int compare(BitbucketCloudRepository o1, BitbucketCloudRepository o2) {
return o1.getRepositoryName().compareTo(o2.getRepositoryName());
}
});
return repositories;
}
};
try {
return cachedRepositories.get(cacheKey.toString(), new Callable<List<BitbucketCloudRepository>>() {
@Override
public List<BitbucketCloudRepository> call() throws Exception {
List<BitbucketCloudRepository> repositories = new ArrayList<BitbucketCloudRepository>();
Integer pageNumber = 1;
String url, response;
PaginatedBitbucketRepository page;
do {
response = getRequest(url = template.set("page", pageNumber).expand());
try {
page = JsonParser.toJava(response, PaginatedBitbucketRepository.class);
repositories.addAll(page.getValues());
} catch (IOException e) {
throw new IOException("I/O error when parsing response from URL: " + url, e);
}
pageNumber++;
} while (page.getNext() != null);
Collections.sort(repositories, new Comparator<BitbucketCloudRepository>() {
@Override
public int compare(BitbucketCloudRepository o1, BitbucketCloudRepository o2) {
return o1.getRepositoryName().compareTo(o2.getRepositoryName());
}
});
return repositories;
}
});
} catch (ExecutionException ex) {
if (enableCache) {
return cachedRepositories.get(cacheKey.toString(), request);
} else {
return request.call();
}
} catch (Exception ex) {
throw new IOException("Error while loading repositories from cache", ex);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketApi;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketApiFactory;
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.AbstractBitbucketEndpoint;
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketCloudEndpoint;
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketEndpointConfiguration;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
Expand All @@ -20,6 +22,17 @@ protected boolean isMatch(@Nullable String serverUrl) {
@Override
protected BitbucketApi create(@Nullable String serverUrl, @Nullable StandardUsernamePasswordCredentials credentials,
@NonNull String owner, @CheckForNull String repository) {
return new BitbucketCloudApiClient(owner, repository, credentials);
AbstractBitbucketEndpoint endpoint = BitbucketEndpointConfiguration.get().findEndpoint(BitbucketCloudEndpoint.SERVER_URL);
boolean enableCache = false;
int teamCacheDuration = 0;
int repositoriesCacheDuration = 0;
if (endpoint != null && endpoint instanceof BitbucketCloudEndpoint) {
enableCache = ((BitbucketCloudEndpoint) endpoint).isEnableCache();
teamCacheDuration = ((BitbucketCloudEndpoint) endpoint).getTeamCacheDuration();
repositoriesCacheDuration = ((BitbucketCloudEndpoint) endpoint).getRepositoriesCacheDuration();
}
return new BitbucketCloudApiClient(
enableCache, teamCacheDuration, repositoriesCacheDuration,
owner, repository, credentials);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@
*/
package com.cloudbees.jenkins.plugins.bitbucket.client;

import static java.util.concurrent.TimeUnit.NANOSECONDS;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
Expand All @@ -35,7 +40,7 @@ public class Cache<K, V> {

private final Map<K, Entry<V>> entries;

private final long expireAfterNanos;
private long expireAfterNanos;

public Cache(final int duration, final TimeUnit unit) {
this(duration, unit, MAX_ENTRIES_DEFAULT);
Expand Down Expand Up @@ -73,6 +78,10 @@ public int size() {
return entries.size();
}

public void setExpireDuration(final int duration, final TimeUnit unit) {
this.expireAfterNanos = unit.toNanos(duration);
}

private boolean isExpired(final K key) {
final Entry<V> entry = entries.get(key);
return entry != null && System.nanoTime() - entry.nanos > expireAfterNanos;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,18 @@
*/
package com.cloudbees.jenkins.plugins.bitbucket.endpoints;

import com.cloudbees.jenkins.plugins.bitbucket.client.BitbucketCloudApiClient;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.damnhandy.uri.template.UriTemplate;

import java.util.List;

import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.Util;
import hudson.util.FormValidation;

import javax.annotation.Nonnull;
import org.kohsuke.stapler.DataBoundConstructor;

Expand All @@ -48,18 +54,55 @@ public class BitbucketCloudEndpoint extends AbstractBitbucketEndpoint {
*/
public static final String BAD_SERVER_URL = "http://bitbucket.org";

/**
* {@code true} if caching should be used to reduce requests to Bitbucket.
*/
private final boolean enableCache;

/**
* How long, in minutes, to cache the team response.
*/
private final int teamCacheDuration;

/**
* How long, in minutes, to cache the repositories response.
*/
private final int repositoriesCacheDuration;

public BitbucketCloudEndpoint(boolean manageHooks, @CheckForNull String credentialsId) {
this(false, 0, 0, manageHooks, credentialsId);
}

/**
* Constructor.
*
* @param enableCache {@code true} if caching should be used to reduce requests to Bitbucket.
* @param teamCacheDuration How long, in minutes, to cache the team response.
* @param repositoriesCacheDuration How long, in minutes, to cache the repositories response.
* @param manageHooks {@code true} if and only if Jenkins is supposed to auto-manage hooks for this end-point.
* @param credentialsId The {@link StandardUsernamePasswordCredentials#getId()} of the credentials to use for
* auto-management of hooks.
*/
@DataBoundConstructor
public BitbucketCloudEndpoint(boolean manageHooks, @CheckForNull String credentialsId) {
public BitbucketCloudEndpoint(boolean enableCache, int teamCacheDuration, int repositoriesCacheDuration, boolean manageHooks, @CheckForNull String credentialsId) {
super(manageHooks, credentialsId);
this.enableCache = enableCache;
this.teamCacheDuration = teamCacheDuration;
this.repositoriesCacheDuration = repositoriesCacheDuration;
}

public boolean isEnableCache() {
return enableCache;
}

public int getTeamCacheDuration() {
return teamCacheDuration;
}

public int getRepositoriesCacheDuration() {
return repositoriesCacheDuration;
}

/**
* {@inheritDoc}
*/
Expand Down Expand Up @@ -103,5 +146,10 @@ public static class DescriptorImpl extends AbstractBitbucketEndpointDescriptor {
public String getDisplayName() {
return Messages.BitbucketCloudEndpoint_displayName();
}

public FormValidation doClear() {
BitbucketCloudApiClient.clearCaches();
return FormValidation.ok("done");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import jenkins.scm.api.SCMName;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;

/**
Expand Down Expand Up @@ -70,6 +71,11 @@ public class BitbucketServerEndpoint extends AbstractBitbucketEndpoint {
@NonNull
private final String serverUrl;

/**
* Whether to always call the can merge api when retrieving pull requests.
*/
private boolean callCanMerge = true;

/**
* @param displayName Optional name to use to describe the end-point.
* @param serverUrl The URL of this Bitbucket Server
Expand All @@ -87,6 +93,15 @@ public BitbucketServerEndpoint(@CheckForNull String displayName, @NonNull String
: displayName.trim();
}

public boolean isCallCanMerge() {
return callCanMerge;
}

@DataBoundSetter
public void setCallCanMerge(boolean callCanMerge) {
this.callCanMerge = callCanMerge;
}

/**
* {@inheritDoc}
*/
Expand Down
Loading

0 comments on commit d79fcee

Please sign in to comment.