diff --git a/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java b/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java index 0c76d8a99..86da634c1 100644 --- a/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java +++ b/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java @@ -1,6 +1,7 @@ package org.jenkinsci.plugins.github_branch_source; import com.cloudbees.plugins.credentials.CredentialsScope; +import com.cloudbees.plugins.credentials.CredentialsSnapshotTaker; import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; import com.cloudbees.plugins.credentials.impl.BaseStandardCredentials; import edu.umd.cs.findbugs.annotations.CheckForNull; @@ -8,10 +9,12 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; import hudson.Util; +import hudson.remoting.Channel; import hudson.util.FormValidation; import hudson.util.ListBoxModel; import hudson.util.Secret; import java.io.IOException; +import java.io.Serializable; import java.util.List; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -165,6 +168,58 @@ public String getUsername() { return appID; } + /** + * Ensures that the credentials state as serialized via Remoting to an agent includes fields which are {@code transient} for purposes of XStream. + * This provides a ~2× performance improvement over reconstructing the object without that state, + * in the normal case that {@link #cachedToken} is valid and will remain valid for the brief time that elapses before the agent calls {@link #getPassword}: + * + * @see CredentialsSnapshotTaker + */ + private Object writeReplace() { + if (/* XStream */Channel.current() == null) { + return this; + } + return new Replacer(this); + } + + private static final class Replacer implements Serializable { + + private final CredentialsScope scope; + private final String id; + private final String description; + private final String appID; + private final Secret privateKey; + private final String apiUri; + private final String owner; + private final String cachedToken; + private final long tokenCacheTime; + + Replacer(GitHubAppCredentials onMaster) { + scope = onMaster.getScope(); + id = onMaster.getId(); + description = onMaster.getDescription(); + appID = onMaster.appID; + privateKey = onMaster.privateKey; + apiUri = onMaster.apiUri; + owner = onMaster.owner; + cachedToken = onMaster.cachedToken; + tokenCacheTime = onMaster.tokenCacheTime; + } + + private Object readResolve() { + GitHubAppCredentials clone = new GitHubAppCredentials(scope, id, description, appID, privateKey); + clone.apiUri = apiUri; + clone.owner = owner; + clone.cachedToken = cachedToken; + clone.tokenCacheTime = tokenCacheTime; + return clone; + } + + } + /** * {@inheritDoc} */