From 37da6daf780a7ea0805a9cbd4d3c3b356c8f5d56 Mon Sep 17 00:00:00 2001 From: Stephen Connolly Date: Wed, 12 Apr 2017 15:12:09 +0100 Subject: [PATCH] [JENKINS-43507] Refactoring to enable traits --- src/main/java/jenkins/scm/api/SCMBuilder.java | 56 ++++++++ .../jenkins/scm/api/trait/SCMHeadFilter.java | 10 ++ .../scm/api/trait/SCMSourceRequest.java | 126 ++++++++++++++++++ .../api/trait/SCMSourceRequestBuilder.java | 112 ++++++++++++++++ .../jenkins/scm/api/trait/SCMSourceTrait.java | 102 ++++++++++++++ .../api/trait/SCMSourceTraitDescriptor.java | 58 ++++++++ .../jenkins/scm/api/trait/package-info.java | 29 ++++ 7 files changed, 493 insertions(+) create mode 100644 src/main/java/jenkins/scm/api/SCMBuilder.java create mode 100644 src/main/java/jenkins/scm/api/trait/SCMHeadFilter.java create mode 100644 src/main/java/jenkins/scm/api/trait/SCMSourceRequest.java create mode 100644 src/main/java/jenkins/scm/api/trait/SCMSourceRequestBuilder.java create mode 100644 src/main/java/jenkins/scm/api/trait/SCMSourceTrait.java create mode 100644 src/main/java/jenkins/scm/api/trait/SCMSourceTraitDescriptor.java create mode 100644 src/main/java/jenkins/scm/api/trait/package-info.java diff --git a/src/main/java/jenkins/scm/api/SCMBuilder.java b/src/main/java/jenkins/scm/api/SCMBuilder.java new file mode 100644 index 00000000..78346ac8 --- /dev/null +++ b/src/main/java/jenkins/scm/api/SCMBuilder.java @@ -0,0 +1,56 @@ +package jenkins.scm.api; + +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.model.TaskListener; +import hudson.scm.SCM; +import hudson.scm.SCMDescriptor; +import java.util.Arrays; +import java.util.Collection; +import jenkins.model.Jenkins; +import jenkins.scm.api.trait.SCMSourceTrait; + +public abstract class SCMBuilder,S extends SCM> { + + private final Class clazz; + private final SCMHead head; + private final SCMRevision revision; + + public SCMBuilder(Class clazz, @NonNull SCMHead head, @CheckForNull SCMRevision revision) { + this.clazz = clazz; + this.head = head; + this.revision = revision; + } + + public SCMHead getHead() { + return head; + } + + public SCMRevision getRevision() { + return revision; + } + + public abstract S build(); + + @SuppressWarnings("unchecked") + public B withTrait(@NonNull SCMSourceTrait trait) { + trait.applyToSCM((B)this); + return (B) this; + } + + public B withTraits(@NonNull SCMSourceTrait... traits) { + return withTraits(Arrays.asList(traits)); + } + + @SuppressWarnings("unchecked") + public B withTraits(@NonNull Collection traits) { + for (SCMSourceTrait trait : traits) { + withTrait(trait); + } + return (B) this; + } + + public SCMDescriptor getSCMDescriptor() { + return (SCMDescriptor)Jenkins.getActiveInstance().getDescriptorOrDie(clazz); + } +} diff --git a/src/main/java/jenkins/scm/api/trait/SCMHeadFilter.java b/src/main/java/jenkins/scm/api/trait/SCMHeadFilter.java new file mode 100644 index 00000000..19b7565f --- /dev/null +++ b/src/main/java/jenkins/scm/api/trait/SCMHeadFilter.java @@ -0,0 +1,10 @@ +package jenkins.scm.api.trait; + +import edu.umd.cs.findbugs.annotations.NonNull; +import jenkins.scm.api.SCMHead; + +public abstract class SCMHeadFilter { + + public abstract boolean isExcluded(@NonNull SCMSourceRequest request, @NonNull SCMHead head); + +} diff --git a/src/main/java/jenkins/scm/api/trait/SCMSourceRequest.java b/src/main/java/jenkins/scm/api/trait/SCMSourceRequest.java new file mode 100644 index 00000000..0ea59ff8 --- /dev/null +++ b/src/main/java/jenkins/scm/api/trait/SCMSourceRequest.java @@ -0,0 +1,126 @@ +/* + * The MIT License + * + * Copyright (c) 2017 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package jenkins.scm.api.trait; + +import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.model.TaskListener; +import java.io.Closeable; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import jenkins.scm.api.SCMHead; +import jenkins.scm.api.SCMHeadEvent; +import jenkins.scm.api.SCMHeadObserver; +import jenkins.scm.api.SCMRevision; +import jenkins.scm.api.SCMSource; +import jenkins.scm.api.SCMSourceCriteria; +import jenkins.scm.api.mixin.SCMHeadMixin; + +/** + * Represents the context of an individual request for a call to + * {@link SCMSource#retrieve(SCMSourceCriteria, SCMHeadObserver, SCMHeadEvent, TaskListener)} or an equivalent method. + * + * @since 2.2.0 + */ +public abstract class SCMSourceRequest implements Closeable { + + private static final Set> STANDARD_MIXINS = + Collections.>singleton(SCMHeadMixin.class); + private final List filters; + + private final List criteria; + + private final TaskListener listener; + + private final SCMHeadObserver observer; + + private final Set observerIncludes; + + protected SCMSourceRequest(SCMSourceRequestBuilder builder) { + this.filters = builder.filters(); + this.criteria = builder.criteria().isEmpty() + ? Collections.emptyList() + : Collections.unmodifiableList(new ArrayList(builder.criteria())); + this.observer = builder.observer(); + this.observerIncludes = observer.getIncludes(); + this.listener = builder.listener(); + } + + public boolean isExcluded(SCMHead head) { + if (observerIncludes != null && !observerIncludes.contains(head)) { + return true; + } + if (filters.isEmpty()) { + return false; + } + for (SCMHeadFilter filter : filters) { + if (filter.isExcluded(this, head)) { + return true; + } + } + return false; + } + + @NonNull + public List getCriteria() { + return criteria; + } + + public boolean hasNoCriteria() { + return criteria.isEmpty(); + } + + public boolean meetsCriteria(SCMSourceCriteria.Probe probe) throws IOException { + for (SCMSourceCriteria c : criteria) { + if (!c.isHead(probe, listener)) { + return false; + } + } + return true; + } + + public void criteriaMet(SCMHead head, SCMRevision revision) throws IOException, InterruptedException { + observer.observe(head, revision); + } + + public boolean isComplete() { + return !observer.isObserving(); + } + + public TaskListener listener() { + return listener; + } + + /** + * {@inheritDoc} + */ + @Override + public void close() throws IOException { + // default to no-op but allow subclasses to store persistent connections in the request and clean up after + } +} diff --git a/src/main/java/jenkins/scm/api/trait/SCMSourceRequestBuilder.java b/src/main/java/jenkins/scm/api/trait/SCMSourceRequestBuilder.java new file mode 100644 index 00000000..1e6967b4 --- /dev/null +++ b/src/main/java/jenkins/scm/api/trait/SCMSourceRequestBuilder.java @@ -0,0 +1,112 @@ +/* + * The MIT License + * + * Copyright (c) 2017 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package jenkins.scm.api.trait; + +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.model.TaskListener; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import jenkins.scm.api.SCMHeadObserver; +import jenkins.scm.api.SCMSource; +import jenkins.scm.api.SCMSourceCriteria; + +public abstract class SCMSourceRequestBuilder,R extends SCMSourceRequest> { + @NonNull + private final List criteria = new ArrayList(); + @NonNull + private final List filters = new ArrayList(); + @NonNull + private final TaskListener listener; + @NonNull + private SCMHeadObserver observer; + + public SCMSourceRequestBuilder(@CheckForNull SCMSourceCriteria criteria, @NonNull SCMHeadObserver observer, @NonNull TaskListener listener) { + withCriteria(criteria); + this.listener = listener; + this.observer = observer; + } + + @SuppressWarnings("unchecked") + public B withCriteria(@CheckForNull SCMSourceCriteria criteria) { + if (criteria != null) { + this.criteria.add(criteria); + } + return (B)this; + } + + @SuppressWarnings("unchecked") + public B withFilter(@CheckForNull SCMHeadFilter filter) { + if (filter != null) { + this.filters.add(filter); + } + return (B)this; + } + + @SuppressWarnings("unchecked") + public B withTrait(@NonNull SCMSourceTrait trait) { + observer = trait.applyToObserver(observer); + trait.applyToRequest((B) this); + return (B)this; + } + + public B withTraits(@NonNull SCMSourceTrait... traits) { + return withTraits(Arrays.asList(traits)); + } + + @SuppressWarnings("unchecked") + public B withTraits(@NonNull Collection traits) { + for (SCMSourceTrait trait: traits) { + withTrait(trait); + } + return (B) this; + } + + @NonNull + public TaskListener listener() { + return listener; + } + + @NonNull + public List criteria() { + return Collections.unmodifiableList(criteria); + } + + @NonNull + public List filters() { + return Collections.unmodifiableList(filters); + } + + @NonNull + public SCMHeadObserver observer() { + return observer; + } + + public abstract R build(); +} diff --git a/src/main/java/jenkins/scm/api/trait/SCMSourceTrait.java b/src/main/java/jenkins/scm/api/trait/SCMSourceTrait.java new file mode 100644 index 00000000..4ef2fb09 --- /dev/null +++ b/src/main/java/jenkins/scm/api/trait/SCMSourceTrait.java @@ -0,0 +1,102 @@ +/* + * The MIT License + * + * Copyright (c) 2017 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package jenkins.scm.api.trait; + +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.model.AbstractDescribableImpl; +import hudson.model.TaskListener; +import hudson.scm.SCM; +import hudson.util.LogTaskListener; +import java.util.logging.Level; +import java.util.logging.Logger; +import jenkins.scm.api.SCMBuilder; +import jenkins.scm.api.SCMHeadCategory; +import jenkins.scm.api.SCMHeadObserver; + + +public class SCMSourceTrait extends AbstractDescribableImpl { + + public final , R extends SCMSourceRequest> void applyToRequest(B builder) { + if (getDescriptor().isApplicableTo(builder.getClass())) { + // guard against non-applicable + decorateRequest(builder); + } + } + + protected , R extends SCMSourceRequest> void decorateRequest(B builder) { + } + + @NonNull + public final SCMHeadObserver applyToObserver(@NonNull SCMHeadObserver observer) { + return decorateObserver(observer); + } + + @NonNull + public SCMHeadObserver decorateObserver(@NonNull SCMHeadObserver observer) { + return observer; + } + + public final , S extends SCM> void applyToSCM(B builder) { + if (!getDescriptor().isApplicableTo(builder.getSCMDescriptor())) { + // guard against non-applicable + } + decorateSCM(builder); + } + + protected , S extends SCM> void decorateSCM(B builder) { + } + + public boolean isCategoryEnabled(@NonNull SCMHeadCategory category) { + return true; + } + + @Override + public SCMSourceTraitDescriptor getDescriptor() { + return (SCMSourceTraitDescriptor) super.getDescriptor(); + } + + /** + * Turns a possibly {@code null} {@link TaskListener} reference into a guaranteed non-null reference. + * + * @param listener a possibly {@code null} {@link TaskListener} reference. + * @return guaranteed non-null {@link TaskListener}. + */ + @NonNull + private TaskListener defaultListener(@CheckForNull TaskListener listener) { + if (listener == null) { + Level level; + try { + level = Level.parse(System.getProperty(getClass().getName() + ".defaultListenerLevel", "FINE")); + } catch (IllegalArgumentException e) { + level = Level.FINE; + } + return new LogTaskListener(Logger.getLogger(getClass().getName()), level); + } + return listener; + } + +} diff --git a/src/main/java/jenkins/scm/api/trait/SCMSourceTraitDescriptor.java b/src/main/java/jenkins/scm/api/trait/SCMSourceTraitDescriptor.java new file mode 100644 index 00000000..a92b13e6 --- /dev/null +++ b/src/main/java/jenkins/scm/api/trait/SCMSourceTraitDescriptor.java @@ -0,0 +1,58 @@ +/* + * The MIT License + * + * Copyright (c) 2017 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package jenkins.scm.api.trait; + +import hudson.model.Descriptor; +import hudson.scm.SCM; +import hudson.scm.SCMDescriptor; + +/** + * Abstract base class for {@link Descriptor} of {@link SCMSourceTrait} implementations. + * + * @since 2.2.0 + */ +public abstract class SCMSourceTraitDescriptor extends Descriptor { + + /** + * Checks if the {@link SCMSourceTrait} is relevant to the specified type of {@link SCMSourceRequestBuilder}. + * + * @param requestBuilderClass the type of {@link SCMSourceRequestBuilder}. + * @return {@code true} if applicable to the specified type of {@link SCMSourceRequestBuilder}. + */ + public boolean isApplicableTo(Class requestBuilderClass) { + return true; + } + + /** + * Checks if the {@link SCMSourceTrait} is relevant to the specified {@link SCM}. + * + * @param scm the {@link SCMDescriptor} for the type of {@link SCM}. + * @return {@code true} if applicable to the specified type of {@link SCM}. + */ + public boolean isApplicableTo(SCMDescriptor scm) { + return true; + } +} diff --git a/src/main/java/jenkins/scm/api/trait/package-info.java b/src/main/java/jenkins/scm/api/trait/package-info.java new file mode 100644 index 00000000..cd5eac39 --- /dev/null +++ b/src/main/java/jenkins/scm/api/trait/package-info.java @@ -0,0 +1,29 @@ +/* + * The MIT License + * + * Copyright (c) 2017 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +/** + * @author Stephen Connolly + */ +package jenkins.scm.api.trait;