/* * The MIT License * * Copyright 2015 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.branch; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; import hudson.ExtensionPoint; import hudson.Util; import hudson.model.AbstractDescribableImpl; import hudson.model.ItemGroup; import hudson.model.TaskListener; import java.io.IOException; import java.util.List; import java.util.Map; import jenkins.scm.api.SCMHead; import jenkins.scm.api.SCMHeadEvent; import jenkins.scm.api.SCMHeadObserver; import jenkins.scm.api.SCMSource; import jenkins.scm.api.SCMSourceCriteria; /** * Creates {@link MultiBranchProject}s for repositories where recognized. * * Please define a 'getting-started' view for a subclass, if you would like to provide specific information to the user * how to get started using the type of project factory. This view is displayed when there are no subfolders found. * * @see OrganizationFolder#getProjectFactories * @since 0.2-beta-5 */ public abstract class MultiBranchProjectFactory extends AbstractDescribableImpl<MultiBranchProjectFactory> implements ExtensionPoint { // TODO refactor this class to use a clean API/SPI separation /** * Determines whether this factory recognizes a given configuration. * * @param parent a folder * @param name a project name * @param scmSources a set of SCM sources as added by * {@link jenkins.scm.api.SCMSourceObserver.ProjectObserver#addSource} * @param attributes a set of metadata attributes as added by * {@link jenkins.scm.api.SCMSourceObserver.ProjectObserver#addAttribute} * @param listener a way of reporting progress * @return true if recognized * @throws InterruptedException if interrupted. * @throws IOException if there was an IO error. */ @SuppressWarnings("deprecation") public boolean recognizes(@NonNull ItemGroup<?> parent, @NonNull String name, @NonNull List<? extends SCMSource> scmSources, @NonNull Map<String, Object> attributes, @NonNull TaskListener listener) throws IOException, InterruptedException { return recognizes(parent, name, scmSources, attributes, null, listener); } /** * Determines whether this factory recognizes a given configuration scoped to a specific {@link SCMHeadEvent}. * * @param parent a folder * @param name a project name * @param scmSources a set of SCM sources as added by * {@link jenkins.scm.api.SCMSourceObserver.ProjectObserver#addSource} * @param attributes a set of metadata attributes as added by * {@link jenkins.scm.api.SCMSourceObserver.ProjectObserver#addAttribute} * @param event the {@link SCMHeadEvent} that the recognition test should be restricted to. * @param listener a way of reporting progress * @return true if recognized * @throws InterruptedException if interrupted. * @throws IOException if there was an IO error. * @since 2.0 */ public boolean recognizes(@NonNull ItemGroup<?> parent, @NonNull String name, @NonNull List<? extends SCMSource> scmSources, @NonNull Map<String, Object> attributes, @CheckForNull SCMHeadEvent<?> event, @NonNull TaskListener listener) throws IOException, InterruptedException { if (Util.isOverridden(MultiBranchProjectFactory.class, getClass(), "recognizes", ItemGroup.class, String.class, List.class, Map.class, TaskListener.class)) { return recognizes(parent, name, scmSources, attributes, listener); } else if (Util.isOverridden(MultiBranchProjectFactory.class, getClass(), "createProject", ItemGroup.class, String.class, List.class, Map.class, TaskListener.class)) { return createProject(parent, name, scmSources, attributes, listener) != null; } else { throw new AbstractMethodError(getClass().getName() + " must override recognizes(ItemGroup,String,Map,SCMHeadEvent,TaskListener)"); } } /** * Creates a new multibranch project which matches {@link #recognizes}. * * @param parent a folder * @param name a project name * @param scmSources a set of SCM sources as added by * {@link jenkins.scm.api.SCMSourceObserver.ProjectObserver#addSource} * @param attributes a set of metadata attributes as added by * {@link jenkins.scm.api.SCMSourceObserver.ProjectObserver#addAttribute} * @param listener a way of reporting progress * @return a new uninitialized multibranch project (do not configure its * {@link MultiBranchProject#getSourcesList} or call {@link MultiBranchProject#onCreatedFromScratch}) * @throws InterruptedException if interrupted. * @throws IOException if there was an IO error. */ @NonNull @SuppressWarnings("deprecation") public MultiBranchProject<?, ?> createNewProject(@NonNull ItemGroup<?> parent, @NonNull String name, @NonNull List<? extends SCMSource> scmSources, @NonNull Map<String, Object> attributes, @NonNull TaskListener listener) throws IOException, InterruptedException { if (Util.isOverridden(MultiBranchProjectFactory.class, getClass(), "createProject", ItemGroup.class, String.class, List.class, Map.class, TaskListener.class)) { MultiBranchProject<?, ?> p = createProject(parent, name, scmSources, attributes, listener); if (p == null) { throw new IOException("recognized project " + name + " before, but now"); } return p; } else { throw new AbstractMethodError(getClass().getName() + " must override createNewProject"); } } /** * Updates an existing project which matches {@link #recognizes}. * * @param project an existing project, perhaps created by this factory, perhaps not * @param attributes a set of metadata attributes as added by * {@link jenkins.scm.api.SCMSourceObserver.ProjectObserver#addAttribute} * @param listener a way of reporting progress * @throws InterruptedException if interrupted. * @throws IOException if there was an IO error. */ public void updateExistingProject(@NonNull MultiBranchProject<?, ?> project, @NonNull Map<String, Object> attributes, @NonNull TaskListener listener) throws IOException, InterruptedException { } /** * Creates a new multibranch project which matches {@link #recognizes}. * * @param parent a folder * @param name a project name * @param scmSources a set of SCM sources as added by * {@link jenkins.scm.api.SCMSourceObserver.ProjectObserver#addSource} * @param attributes a set of metadata attributes as added by * {@link jenkins.scm.api.SCMSourceObserver.ProjectObserver#addAttribute} * @param listener a way of reporting progress * @return a new uninitialized multibranch project (do not configure its * {@link MultiBranchProject#getSourcesList} or call {@link MultiBranchProject#onCreatedFromScratch}) * @throws InterruptedException if interrupted. * @throws IOException if there was an IO error. * @deprecated use {@link #createNewProject(ItemGroup, String, List, Map, TaskListener)} */ @Deprecated @CheckForNull public MultiBranchProject<?, ?> createProject(@NonNull ItemGroup<?> parent, @NonNull String name, @NonNull List<? extends SCMSource> scmSources, @NonNull Map<String, Object> attributes, @NonNull TaskListener listener) throws IOException, InterruptedException { if (recognizes(parent, name, scmSources, attributes, listener)) { return createNewProject(parent, name, scmSources, attributes, listener); } else { return null; } } /** * {@inheritDoc} */ @Override public MultiBranchProjectFactoryDescriptor getDescriptor() { return (MultiBranchProjectFactoryDescriptor) super.getDescriptor(); } /** * Creates a particular kind of multibranch project insofar as at least one {@link SCMHead} satisfies a probe. */ public static abstract class BySCMSourceCriteria extends MultiBranchProjectFactory { /** * Defines how to decide whether or not a given repository should host our type of project. * * @param source a repository * @return criteria for treating its branches as a match (or {@code null} if all branches match) */ @CheckForNull protected abstract SCMSourceCriteria getSCMSourceCriteria(@NonNull SCMSource source); /** * Historical alias for {@link #createNewProject}. * @param parent a folder * @param name a project name * {@link jenkins.scm.api.SCMSourceObserver.ProjectObserver#addSource} * @param attributes a set of metadata attributes as added by * {@link jenkins.scm.api.SCMSourceObserver.ProjectObserver#addAttribute} * @return a new uninitialized multibranch project (do not configure its * {@link MultiBranchProject#getSourcesList} or call {@link MultiBranchProject#onCreatedFromScratch}) * @throws InterruptedException if interrupted. * @throws IOException if there was an IO error. */ @NonNull protected abstract MultiBranchProject<?, ?> doCreateProject(@NonNull ItemGroup<?> parent, @NonNull String name, @NonNull Map<String, Object> attributes) throws IOException, InterruptedException; /** * {@inheritDoc} */ @Override public final MultiBranchProject<?, ?> createNewProject(@NonNull ItemGroup<?> parent, @NonNull String name, @NonNull List<? extends SCMSource> scmSources, @NonNull Map<String, Object> attributes, @NonNull TaskListener listener) throws IOException, InterruptedException { return doCreateProject(parent, name, attributes); } /** * {@inheritDoc} */ @Override public boolean recognizes(@NonNull ItemGroup<?> parent, @NonNull String name, @NonNull List<? extends SCMSource> scmSources, @NonNull Map<String, Object> attributes, @NonNull TaskListener listener) throws IOException, InterruptedException { return recognizes(parent, name, scmSources, attributes, null, listener); } /** * {@inheritDoc} */ @Override public boolean recognizes(@NonNull ItemGroup<?> parent, @NonNull String name, @NonNull List<? extends SCMSource> scmSources, @NonNull Map<String, Object> attributes, @CheckForNull SCMHeadEvent<?> event, @NonNull TaskListener listener) throws IOException, InterruptedException { for (final SCMSource scmSource : scmSources) { if (scmSource.fetch(getSCMSourceCriteria(scmSource), SCMHeadObserver.any(), event, listener) .getRevision() != null) { return true; } } return false; } } }