Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[JENKINS-43507] Documenting SCMHeadAuthority
  • Loading branch information
stephenc committed May 4, 2017
1 parent 4d86d11 commit 4ce900a
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 6 deletions.
101 changes: 98 additions & 3 deletions src/main/java/jenkins/scm/api/trait/SCMHeadAuthority.java
Expand Up @@ -24,31 +24,126 @@

package jenkins.scm.api.trait;

import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.OverrideMustInvoke;
import hudson.model.AbstractDescribableImpl;
import java.io.IOException;
import jenkins.scm.api.SCMHead;
import jenkins.scm.api.SCMRevision;
import jenkins.scm.api.mixin.SCMHeadMixin;

public abstract class SCMHeadAuthority<S extends SCMSourceRequest, H extends SCMHeadMixin>
extends AbstractDescribableImpl<SCMHeadAuthority<?, ?>> {
/**
* Abstraction to allow plugable definitions of trust for {@link SCMHead} and {@link SCMRevision} instances in the
* context of a specific {@link SCMSourceRequest}.
* <p>
* Note: there can be multiple authorities for the same types of head / revision active in the context of any one
* request. The ultimate trust state is determined by a logical OR operation, in other words if any one authority
* says that the head / revision is trusted then the head / revision is trusted.
*
* @param <S> the type of {@link SCMSourceRequest}.
* @param <H> the specialization of {@link SCMHeadMixin} that this authority provides trust information for.
* @param <R> the specialization of {@link SCMRevision} that this authority provides trust information for.
* @since 3.4.0
*/
public abstract class SCMHeadAuthority<S extends SCMSourceRequest, H extends SCMHeadMixin, R extends SCMRevision>
extends AbstractDescribableImpl<SCMHeadAuthority<?, ?, ?>> {

/**
* Checks if this authority is relevant to the supplied {@link SCMHead}.
*
* @param head the supplied {@link SCMHead}.
* @return {@code true} if and only if this authority can provide trust information for the supplied head.
*/
public final boolean isApplicableTo(@NonNull SCMHead head) {
return getDescriptor().isApplicableToHead(head);
}

/**
* Checks if this authority is relevant to the supplied {@link SCMRevision}.
*
* @param revision the supplied {@link SCMRevision}.
* @return {@code true} if and only if this authority can provide trust information for the supplied revision.
*/
public final boolean isApplicableTo(@NonNull SCMRevision revision) {
return getDescriptor().isApplicableToRevision(revision);
}

/**
* Checks if this authority is relevant to the supplied {@link SCMSourceRequest}.
*
* @param request the supplied {@link SCMSourceRequest}.
* @return {@code true} if and only if this authority can provide trust information for the supplied requesy.
*/
public final boolean isApplicableTo(@NonNull SCMSourceRequest request) {
return getDescriptor().isApplicableToRequest(request);
}

/**
* Checks if the supplied {@link SCMHead} is trusted in the context of the specified {@link SCMSourceRequest}.
*
* @param request the {@link SCMSourceRequest}.
* @param head the {@link SCMHead}.
* @return {@code true} if the supplied head is trusted.
* @throws IOException if there was an I/O error trying to establish the trust status.
* @throws InterruptedException if interrupted while trying to establing the trust status.
*/
@SuppressWarnings("unchecked")
public final boolean isTrusted(@NonNull SCMSourceRequest request, @NonNull SCMHead head)
throws IOException, InterruptedException {
return isApplicableTo(request) && isApplicableTo(head) && checkTrusted((S) request, (H) head);
return isApplicableTo(request)
&& isApplicableTo(head)
&& checkTrusted((S) request, (H) head);
}

/**
* Checks if the supplied {@link SCMRevision} is trusted in the context of the specified {@link SCMSourceRequest}.
*
* @param request the {@link SCMSourceRequest}.
* @param revision the {@link SCMRevision}.
* @return {@code true} if the supplied revision is trusted.
* @throws IOException if there was an I/O error trying to establish the trust status.
* @throws InterruptedException if interrupted while trying to establing the trust status.
*/
@SuppressWarnings("unchecked")
public final boolean isTrusted(@NonNull SCMSourceRequest request, @CheckForNull SCMRevision revision)
throws IOException, InterruptedException {
return isApplicableTo(request)
&& isApplicableTo(revision.getHead())
&& isApplicableTo(revision)
&& checkTrusted((S) request, (H) revision);
}

/**
* SPI: checks if the supplied {@link SCMHead} is trusted in the context of the supplied {@link SCMSourceRequest}.
*
* @param request the {@link SCMSourceRequest}.
* @param head the {@link SCMHead}.
* @return {@code true} if trusted.
* @throws IOException if there was an I/O error trying to establish the trust status.
* @throws InterruptedException if interrupted while trying to establing the trust status.
*/
protected abstract boolean checkTrusted(@NonNull S request, @NonNull H head) throws IOException, InterruptedException;

/**
* SPI: checks if the supplied {@link SCMRevision} is trusted in the context of the supplied
* {@link SCMSourceRequest}. Default implementation just calls {@link #checkTrusted(SCMSourceRequest, SCMHeadMixin)}
* with {@link SCMRevision#getHead()}.
*
* @param request the {@link SCMSourceRequest}.
* @param revision the {@link SCMRevision}.
* @return {@code true} if trusted.
* @throws IOException if there was an I/O error trying to establish the trust status.
* @throws InterruptedException if interrupted while trying to establing the trust status.
*/
@OverrideMustInvoke
protected boolean checkTrusted(@NonNull S request, @NonNull R revision) throws IOException, InterruptedException {
return checkTrusted(request, (H) revision.getHead());
}

/**
* {@inheritDoc}
*/
@Override
public SCMHeadAuthorityDescriptor getDescriptor() {
return (SCMHeadAuthorityDescriptor) super.getDescriptor();
Expand Down
Expand Up @@ -28,29 +28,61 @@
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import jenkins.scm.api.SCMHead;
import jenkins.scm.api.SCMRevision;
import jenkins.scm.api.mixin.SCMHeadMixin;
import org.jvnet.tiger_types.Types;

public abstract class SCMHeadAuthorityDescriptor extends Descriptor<SCMHeadAuthority<?, ?>> {
/**
* {@link Descriptor} base class for {@link SCMHeadAuthority} implementations.
*
* @since 3.4.0
*/
public abstract class SCMHeadAuthorityDescriptor extends Descriptor<SCMHeadAuthority<?, ?, ?>> {

/**
* The {@link SCMSourceRequest} class.
*/
private final Class<? extends SCMSourceRequest> requestClass;
/**
* The {@link SCMHeadMixin} class.
*/
private final Class<? extends SCMHeadMixin> headClass;
/**
* The {@link SCMRevision} class.
*/
private final Class<? extends SCMRevision> revisionClass;

protected SCMHeadAuthorityDescriptor(Class<? extends SCMHeadAuthority<?, ?>> clazz,
/**
* Constructor to use when type inferrence using {@link #SCMHeadAuthorityDescriptor()} does not work.
*
* @param clazz Pass in the type of {@link SCMHeadAuthority}
* @param requestClass the type of {@link SCMSourceRequest}.
* @param headClass the type of {@link SCMHead}.
* @param revisionClass the type of {@link SCMRevision}.
*/
protected SCMHeadAuthorityDescriptor(Class<? extends SCMHeadAuthority<?, ?, ?>> clazz,
Class<? extends SCMSourceRequest> requestClass,
Class<? extends SCMHeadMixin> headClass) {
Class<? extends SCMHeadMixin> headClass,
Class<? extends SCMRevision> revisionClass) {
super(clazz);
this.requestClass = requestClass;
this.headClass = headClass;
this.revisionClass = revisionClass;
}

/**
* Infers the type of the corresponding {@link SCMHeadAuthority} from the outer class.
* This version works when you follow the common convention, where a descriptor
* is written as the static nested class of the describable class.
*/
protected SCMHeadAuthorityDescriptor() {
Type bt = Types.getBaseClass(clazz, SCMHeadAuthority.class);
if (bt instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) bt;
// this 'headClass' is the closest approximation of T of SCMHeadAuthority<T>.
requestClass = Types.erasure(pt.getActualTypeArguments()[0]);
headClass = Types.erasure(pt.getActualTypeArguments()[1]);
revisionClass = Types.erasure(pt.getActualTypeArguments()[2]);
if (!SCMSourceRequest.class.isAssignableFrom(requestClass)) {
throw new AssertionError(
"Failed to correctly detect SCMSourceRequest specialization. Use the constructor that takes "
Expand All @@ -62,25 +94,75 @@ protected SCMHeadAuthorityDescriptor() {
"Failed to correctly detect SCMHead specialization. Use the constructor that takes the Class "
+ "objects explicitly");
}
if (!SCMRevision.class.isAssignableFrom(revisionClass)) {
throw new AssertionError(
"Failed to correctly detect SCMRevision specialization. Use the constructor that takes the "
+ "Class "
+ "objects explicitly");
}
} else {
throw new AssertionError(
"Failed to correctly detect specialization. Use the constructor that takes the Class objects "
+ "explicitly");
}
}

/**
* Checks if this {@link SCMHeadAuthorityDescriptor} is applicable to the supplied {@link SCMHead}.
*
* @param head the {@link SCMHead}.
* @return {@code true} if applicable.
*/
public boolean isApplicableToHead(SCMHead head) {
return isApplicableToHead(head.getClass());
}

/**
* Checks if this {@link SCMHeadAuthorityDescriptor} is applicable to the supplied type of {@link SCMHead}.
*
* @param headClass the type of {@link SCMHead}.
* @return {@code true} if applicable.
*/
public boolean isApplicableToHead(Class<? extends SCMHead> headClass) {
return this.headClass.isAssignableFrom(headClass);
}

/**
* Checks if this {@link SCMHeadAuthorityDescriptor} is applicable to the supplied {@link SCMRevision}.
*
* @param revision the {@link SCMRevision}.
* @return {@code true} if applicable.
*/
public boolean isApplicableToRevision(SCMRevision revision) {
return isApplicableToHead(revision.getHead()) && isApplicableToRevision(revision.getClass());
}

/**
* Checks if this {@link SCMHeadAuthorityDescriptor} is applicable to the supplied type of {@link SCMRevision}.
*
* @param revisionClass the type of {@link SCMRevision}.
* @return {@code true} if applicable.
*/
public boolean isApplicableToRevision(Class<? extends SCMRevision> revisionClass) {
return this.revisionClass.isAssignableFrom(revisionClass);
}

/**
* Checks if this {@link SCMHeadAuthorityDescriptor} is applicable to the supplied {@link SCMSourceRequest}.
*
* @param request the {@link SCMSourceRequest}.
* @return {@code true} if applicable.
*/
public boolean isApplicableToRequest(SCMSourceRequest request) {
return requestClass.isInstance(request);
}

/**
* Checks if this {@link SCMHeadAuthorityDescriptor} is applicable to the supplied type of {@link SCMSourceRequest}.
*
* @param requestClass the type of {@link SCMSourceRequest}.
* @return {@code true} if applicable.
*/
public boolean isApplicableToRequest(Class<? extends SCMSourceRequest> requestClass) {
return this.requestClass.isAssignableFrom(requestClass);
}
Expand Down

0 comments on commit 4ce900a

Please sign in to comment.