From b63f162f75964f52118755105bee676d29da8828 Mon Sep 17 00:00:00 2001 From: kohsuke Date: Tue, 24 Feb 2009 22:53:04 +0000 Subject: [PATCH] updated Publisher to support auto-discovery. git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@15683 71c3de6d-444a-0410-be80-ed276b4c234a --- .../java/hudson/DescriptorExtensionList.java | 16 ++++- core/src/main/java/hudson/ExtensionList.java | 26 +++++-- core/src/main/java/hudson/Functions.java | 2 +- .../java/hudson/matrix/MatrixProject.java | 2 +- .../java/hudson/maven/MavenModuleSet.java | 2 +- .../java/hudson/maven/RedeployPublisher.java | 14 ++-- core/src/main/java/hudson/model/Hudson.java | 6 +- core/src/main/java/hudson/model/Project.java | 2 +- .../java/hudson/tasks/ArtifactArchiver.java | 11 +-- .../src/main/java/hudson/tasks/BuildStep.java | 64 ++++++++++++----- .../main/java/hudson/tasks/BuildTrigger.java | 16 ++--- core/src/main/java/hudson/tasks/Builder.java | 1 - .../main/java/hudson/tasks/Fingerprinter.java | 13 ++-- .../java/hudson/tasks/JavadocArchiver.java | 10 +-- core/src/main/java/hudson/tasks/Mailer.java | 14 ++-- core/src/main/java/hudson/tasks/Notifier.java | 50 +++++++++++++ .../src/main/java/hudson/tasks/Publisher.java | 72 ++++++++++++++++++- core/src/main/java/hudson/tasks/Recorder.java | 51 +++++++++++++ .../tasks/junit/JUnitResultArchiver.java | 11 ++- .../test/AggregatedTestResultPublisher.java | 11 ++- .../org/jvnet/hudson/test/HudsonTestCase.java | 6 +- 21 files changed, 289 insertions(+), 111 deletions(-) create mode 100644 core/src/main/java/hudson/tasks/Notifier.java create mode 100644 core/src/main/java/hudson/tasks/Recorder.java diff --git a/core/src/main/java/hudson/DescriptorExtensionList.java b/core/src/main/java/hudson/DescriptorExtensionList.java index 9968bdbdd9..40ca04baa9 100644 --- a/core/src/main/java/hudson/DescriptorExtensionList.java +++ b/core/src/main/java/hudson/DescriptorExtensionList.java @@ -30,6 +30,8 @@ import hudson.model.ViewDescriptor; import hudson.model.Descriptor.FormException; import hudson.util.Memoizer; import hudson.slaves.NodeDescriptor; +import hudson.tasks.Publisher; +import hudson.tasks.Publisher.DescriptorExtensionListImpl; import java.util.List; import java.util.ArrayList; @@ -58,13 +60,23 @@ import net.sf.json.JSONObject; * * @since 1.286 */ -public final class DescriptorExtensionList, D extends Descriptor> extends ExtensionList { +public class DescriptorExtensionList, D extends Descriptor> extends ExtensionList { + /** + * Creates a new instance. + */ + public static ,D extends Descriptor> + DescriptorExtensionList create(Hudson hudson, Class describableType) { + if(describableType==Publisher.class) + return (DescriptorExtensionList)new DescriptorExtensionListImpl(hudson); + return new DescriptorExtensionList(hudson,describableType); + } + /** * Type of the {@link Describable} that this extension list retains. */ private final Class describableType; - public DescriptorExtensionList(Hudson hudson, Class describableType) { + protected DescriptorExtensionList(Hudson hudson, Class describableType) { super(hudson, (Class)Descriptor.class, legacyDescriptors.get(describableType)); this.describableType = describableType; } diff --git a/core/src/main/java/hudson/ExtensionList.java b/core/src/main/java/hudson/ExtensionList.java index ea642ef246..bc17164e03 100644 --- a/core/src/main/java/hudson/ExtensionList.java +++ b/core/src/main/java/hudson/ExtensionList.java @@ -35,7 +35,6 @@ import java.util.Iterator; import java.util.List; import java.util.Vector; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ConcurrentHashMap; /** * Retains the known extension instances for the given type 'T'. @@ -65,7 +64,8 @@ public class ExtensionList extends AbstractList { /** * Once discovered, extensions are retained here. */ - private volatile CopyOnWriteArrayList extensions; + @CopyOnWrite + private volatile List extensions; /** * Place to store manually registered instances with the per-Hudson scope. @@ -119,11 +119,14 @@ public class ExtensionList extends AbstractList { * Prefer automatic registration. */ @Override - public boolean add(T t) { + public synchronized boolean add(T t) { legacyInstances.add(t); // if we've already filled extensions, add it - if(extensions!=null) - extensions.add(t); + if(extensions!=null) { + List r = new ArrayList(extensions); + r.add(t); + extensions = sort(r); + } return true; } @@ -149,7 +152,7 @@ public class ExtensionList extends AbstractList { if(extensions==null) { List r = load(); r.addAll(legacyInstances); - extensions = new CopyOnWriteArrayList(r); + extensions = sort(r); } return extensions; } @@ -165,6 +168,17 @@ public class ExtensionList extends AbstractList { return r; } + /** + * If the {@link ExtensionList} implementation requires sorting extensions, + * override this method to do so. + * + *

+ * The implementation should copy a list, do a sort, and return the new instance. + */ + protected List sort(List r) { + return r; + } + public static ExtensionList create(Hudson hudson, Class type) { if(type==ExtensionFinder.class) return new ExtensionList(hudson,type) { diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java index 1164e341c1..b736f788a2 100644 --- a/core/src/main/java/hudson/Functions.java +++ b/core/src/main/java/hudson/Functions.java @@ -606,7 +606,7 @@ public class Functions { } public static List> getPublisherDescriptors(AbstractProject project) { - return BuildStepDescriptor.filter(BuildStep.PUBLISHERS, project.getClass()); + return BuildStepDescriptor.filter(Publisher.all(), project.getClass()); } public static List> getComputerLauncherDescriptors() { diff --git a/core/src/main/java/hudson/matrix/MatrixProject.java b/core/src/main/java/hudson/matrix/MatrixProject.java index bd06ed3610..b8d09e50b5 100644 --- a/core/src/main/java/hudson/matrix/MatrixProject.java +++ b/core/src/main/java/hudson/matrix/MatrixProject.java @@ -536,7 +536,7 @@ public class MatrixProject extends AbstractProject im buildWrappers.rebuild(req, json, BuildWrappers.getFor(this)); builders.rebuildHetero(req, json, Builder.all(), "builder"); - publishers.rebuild(req, json, BuildStepDescriptor.filter(BuildStep.PUBLISHERS,this.getClass())); + publishers.rebuild(req, json, BuildStepDescriptor.filter(Publisher.all(),this.getClass())); updateTransientActions(); // to pick up transient actions from builder, publisher, etc. rebuildConfigurations(); diff --git a/core/src/main/java/hudson/maven/MavenModuleSet.java b/core/src/main/java/hudson/maven/MavenModuleSet.java index 93be1464ac..9dce8a3a93 100644 --- a/core/src/main/java/hudson/maven/MavenModuleSet.java +++ b/core/src/main/java/hudson/maven/MavenModuleSet.java @@ -604,7 +604,7 @@ public final class MavenModuleSet extends AbstractMavenProject~/.m2/settings.xml for authentication related information. */ @@ -115,16 +117,8 @@ public class RedeployPublisher extends Publisher { return build.getAction(MavenAbstractArtifactRecord.class); } - public BuildStepDescriptor getDescriptor() { - return DESCRIPTOR; - } - - public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); - + @Extension public static class DescriptorImpl extends BuildStepDescriptor { - private DescriptorImpl() { - } - protected DescriptorImpl(Class clazz) { super(clazz); } diff --git a/core/src/main/java/hudson/model/Hudson.java b/core/src/main/java/hudson/model/Hudson.java index e7707c4000..47cd3e775d 100644 --- a/core/src/main/java/hudson/model/Hudson.java +++ b/core/src/main/java/hudson/model/Hudson.java @@ -286,7 +286,7 @@ public final class Hudson extends Node implements ItemGroup, Stapl */ private transient final Memoizer descriptorLists = new Memoizer() { public DescriptorExtensionList compute(Class key) { - return new DescriptorExtensionList(Hudson.this,key); + return DescriptorExtensionList.create(Hudson.this,key); } }; @@ -654,7 +654,7 @@ public final class Hudson extends Node implements ItemGroup, Stapl * Gets the publisher descriptor by name. Primarily used for making them web-visible. */ public Descriptor getPublisher(String shortClassName) { - return findDescriptor(shortClassName, BuildStep.PUBLISHERS); + return findDescriptor(shortClassName, Publisher.all()); } /** @@ -2027,7 +2027,7 @@ public final class Hudson extends Node implements ItemGroup, Stapl for( Descriptor d : Builder.all() ) result &= configureDescriptor(req,json,d); - for( Descriptor d : BuildStep.PUBLISHERS ) + for( Descriptor d : Publisher.all() ) result &= configureDescriptor(req,json,d); for( Descriptor d : BuildWrapper.all() ) diff --git a/core/src/main/java/hudson/model/Project.java b/core/src/main/java/hudson/model/Project.java index 1d28f0db92..bd5429a469 100644 --- a/core/src/main/java/hudson/model/Project.java +++ b/core/src/main/java/hudson/model/Project.java @@ -187,7 +187,7 @@ public abstract class Project

,B extends Build> buildWrappers.rebuild(req,json, BuildWrappers.getFor(this)); builders.rebuildHetero(req,json, Builder.all(), "builder"); - publishers.rebuild(req, json, BuildStepDescriptor.filter(BuildStep.PUBLISHERS, this.getClass())); + publishers.rebuild(req, json, BuildStepDescriptor.filter(Publisher.all(), this.getClass())); updateTransientActions(); // to pick up transient actions from builder, publisher, etc. } diff --git a/core/src/main/java/hudson/tasks/ArtifactArchiver.java b/core/src/main/java/hudson/tasks/ArtifactArchiver.java index 3d0aadc920..eb9cd289cf 100644 --- a/core/src/main/java/hudson/tasks/ArtifactArchiver.java +++ b/core/src/main/java/hudson/tasks/ArtifactArchiver.java @@ -26,6 +26,7 @@ package hudson.tasks; import hudson.FilePath; import hudson.Launcher; import hudson.Util; +import hudson.Extension; import hudson.maven.AbstractMavenProject; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; @@ -48,7 +49,7 @@ import net.sf.json.JSONObject; * * @author Kohsuke Kawaguchi */ -public class ArtifactArchiver extends Publisher { +public class ArtifactArchiver extends Recorder { /** * Comma- or space-separated list of patterns of files/directories to be archived. @@ -146,13 +147,7 @@ public class ArtifactArchiver extends Publisher { return true; } - public Descriptor getDescriptor() { - return DESCRIPTOR; - } - - - public static final Descriptor DESCRIPTOR = new DescriptorImpl(); - + @Extension public static class DescriptorImpl extends BuildStepDescriptor { public String getDisplayName() { return Messages.ArtifactArchiver_DisplayName(); diff --git a/core/src/main/java/hudson/tasks/BuildStep.java b/core/src/main/java/hudson/tasks/BuildStep.java index 27b5d51fa0..8ae91ea01d 100644 --- a/core/src/main/java/hudson/tasks/BuildStep.java +++ b/core/src/main/java/hudson/tasks/BuildStep.java @@ -25,6 +25,7 @@ package hudson.tasks; import hudson.Launcher; import hudson.Extension; +import hudson.ExtensionList; import hudson.util.DescriptorList; import hudson.maven.RedeployPublisher; import hudson.model.AbstractBuild; @@ -34,6 +35,7 @@ import hudson.model.Build; import hudson.model.BuildListener; import hudson.model.Descriptor; import hudson.model.Project; +import hudson.model.Hudson; import hudson.tasks.junit.JUnitResultArchiver; import hudson.tasks.test.AggregatedTestResultPublisher; @@ -41,6 +43,9 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.AbstractList; +import java.util.Iterator; +import java.util.WeakHashMap; /** * One step of the whole build process. @@ -140,25 +145,32 @@ public interface BuildStep { * results, etc. * * @see PublisherList#addNotifier(Descriptor) - * @see PublisherList#addRecorder(Descriptor) + * @see PublisherList#addRecorder(Descriptor) + * + * @deprecated as of 1.286. + * Use {@link Publisher#all()} for read access, and use + * {@link Extension} for registration. */ - public static final PublisherList PUBLISHERS = new PublisherList(Descriptor.toList( - ArtifactArchiver.DESCRIPTOR, - Fingerprinter.DESCRIPTOR, - JavadocArchiver.DESCRIPTOR, - JUnitResultArchiver.DescriptorImpl.DESCRIPTOR, - AggregatedTestResultPublisher.DescriptorImpl.INSTANCE, - BuildTrigger.DESCRIPTOR, - RedeployPublisher.DESCRIPTOR, - Mailer.DESCRIPTOR - )); + public static final PublisherList PUBLISHERS = new PublisherList(); /** * List of publisher descriptor. */ - public static final class PublisherList extends ArrayList> { - public PublisherList(Collection> c) { - super(c); + public static final class PublisherList extends AbstractList> { + /** + * {@link Descriptor}s are actually stored in here. + * Since {@link PublisherList} lives longer than {@link Hudson} we cannot directly use {@link ExtensionList}. + */ + private final DescriptorList core = new DescriptorList(Publisher.class); + + /** + * For descriptors that are manually registered, remember what kind it was since + * older plugins don't extend from neither {@link Recorder} nor {@link Notifier}. + */ + /*package*/ static final WeakHashMap,Class/*either Recorder.class or Notifier.class*/> + KIND = new WeakHashMap, Class>(); + + private PublisherList() { } /** @@ -172,7 +184,8 @@ public interface BuildStep { * @see #addRecorder(Descriptor) */ public void addNotifier( Descriptor d ) { - add(d); + KIND.put(d,Notifier.class); + core.add(d); } /** @@ -185,18 +198,31 @@ public interface BuildStep { * @see #addNotifier(Descriptor) */ public void addRecorder( Descriptor d ) { - int idx = super.indexOf(Mailer.DESCRIPTOR); - add(idx,d); + KIND.put(d,Recorder.class); + core.add(d); } @Override public boolean add(Descriptor d) { - return !contains(d) && super.add(d); + return !contains(d) && core.add(d); } @Override public void add(int index, Descriptor d) { - if(!contains(d)) super.add(index, d); + if(!contains(d)) core.add(d); + } + + public Descriptor get(int index) { + return core.get(index); + } + + public int size() { + return core.size(); + } + + @Override + public Iterator> iterator() { + return core.iterator(); } } } diff --git a/core/src/main/java/hudson/tasks/BuildTrigger.java b/core/src/main/java/hudson/tasks/BuildTrigger.java index 76293ce0af..0b1f01f9e1 100644 --- a/core/src/main/java/hudson/tasks/BuildTrigger.java +++ b/core/src/main/java/hudson/tasks/BuildTrigger.java @@ -24,6 +24,7 @@ package hudson.tasks; import hudson.Launcher; +import hudson.Extension; import hudson.security.AccessControlled; import hudson.matrix.MatrixAggregatable; import hudson.matrix.MatrixAggregator; @@ -33,7 +34,6 @@ import hudson.model.AbstractProject; import hudson.model.BuildListener; import hudson.model.DependecyDeclarer; import hudson.model.DependencyGraph; -import hudson.model.Descriptor; import hudson.model.Hudson; import hudson.model.Item; import hudson.model.Items; @@ -74,7 +74,7 @@ import java.util.logging.Logger; * * @author Kohsuke Kawaguchi */ -public class BuildTrigger extends Publisher implements DependecyDeclarer, MatrixAggregatable { +public class BuildTrigger extends Recorder implements DependecyDeclarer, MatrixAggregatable { /** * Comma-separated list of other projects to be scheduled. @@ -237,13 +237,7 @@ public class BuildTrigger extends Publisher implements DependecyDeclarer, Matrix return this; } - public Descriptor getDescriptor() { - return DESCRIPTOR; - } - - - public static final Descriptor DESCRIPTOR = new DescriptorImpl(); - + @Extension public static class DescriptorImpl extends BuildStepDescriptor { public DescriptorImpl() { Hudson.getInstance().getJobListeners().add(new ItemListener() { @@ -251,8 +245,8 @@ public class BuildTrigger extends Publisher implements DependecyDeclarer, Matrix public void onRenamed(Item item, String oldName, String newName) { // update BuildTrigger of other projects that point to this object. // can't we generalize this? - for( Project p : Hudson.getInstance().getProjects() ) { - BuildTrigger t = (BuildTrigger) p.getPublishers().get(BuildTrigger.DESCRIPTOR); + for( Project p : Hudson.getInstance().getProjects() ) { + BuildTrigger t = p.getPublishersList().get(BuildTrigger.class); if(t!=null) { if(t.onJobRenamed(oldName,newName)) { try { diff --git a/core/src/main/java/hudson/tasks/Builder.java b/core/src/main/java/hudson/tasks/Builder.java index db1f8d4536..0f06d04397 100644 --- a/core/src/main/java/hudson/tasks/Builder.java +++ b/core/src/main/java/hudson/tasks/Builder.java @@ -74,5 +74,4 @@ public abstract class Builder extends BuildStepCompatibilityLayer implements Bui public static DescriptorExtensionList> all() { return Hudson.getInstance().getDescriptorList(Builder.class); } - } diff --git a/core/src/main/java/hudson/tasks/Fingerprinter.java b/core/src/main/java/hudson/tasks/Fingerprinter.java index a18f8f5f66..323ccdde39 100644 --- a/core/src/main/java/hudson/tasks/Fingerprinter.java +++ b/core/src/main/java/hudson/tasks/Fingerprinter.java @@ -27,13 +27,13 @@ import hudson.FilePath; import hudson.FilePath.FileCallable; import hudson.Launcher; import hudson.Util; +import hudson.Extension; import hudson.maven.AbstractMavenProject; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.Action; import hudson.model.Build; import hudson.model.BuildListener; -import hudson.model.Descriptor; import hudson.model.Fingerprint; import hudson.model.Fingerprint.BuildPtr; import hudson.model.FingerprintMap; @@ -67,7 +67,7 @@ import java.util.logging.Logger; * * @author Kohsuke Kawaguchi */ -public class Fingerprinter extends Publisher implements Serializable { +public class Fingerprinter extends Recorder implements Serializable { /** * Comma-separated list of files/directories to be fingerprinted. @@ -103,7 +103,7 @@ public class Fingerprinter extends Publisher implements Serializable { record(build, listener, record, targets); if(recordBuildArtifacts && build instanceof Build) { - ArtifactArchiver aa = (ArtifactArchiver) ((Build)build).getProject().getPublishers().get(ArtifactArchiver.DESCRIPTOR); + ArtifactArchiver aa = ((Build)build).getProject().getPublishersList().get(ArtifactArchiver.class); if(aa==null) { // configuration error listener.error(Messages.Fingerprinter_NoArchiving()); @@ -195,12 +195,7 @@ public class Fingerprinter extends Publisher implements Serializable { } } - public Descriptor getDescriptor() { - return DESCRIPTOR; - } - - public static final Descriptor DESCRIPTOR = new DescriptorImpl(); - + @Extension public static class DescriptorImpl extends BuildStepDescriptor { public String getDisplayName() { return Messages.Fingerprinter_DisplayName(); diff --git a/core/src/main/java/hudson/tasks/JavadocArchiver.java b/core/src/main/java/hudson/tasks/JavadocArchiver.java index b2dca14f9d..f91d712711 100644 --- a/core/src/main/java/hudson/tasks/JavadocArchiver.java +++ b/core/src/main/java/hudson/tasks/JavadocArchiver.java @@ -26,6 +26,7 @@ package hudson.tasks; import hudson.FilePath; import hudson.Launcher; import hudson.Util; +import hudson.Extension; import hudson.maven.AbstractMavenProject; import hudson.model.*; import hudson.util.FormFieldValidator; @@ -43,7 +44,7 @@ import java.io.IOException; * * @author Kohsuke Kawaguchi */ -public class JavadocArchiver extends Publisher { +public class JavadocArchiver extends Recorder { /** * Path to the Javadoc directory in the workspace. */ @@ -115,12 +116,6 @@ public class JavadocArchiver extends Publisher { return new JavadocAction(project); } - public Descriptor getDescriptor() { - return DESCRIPTOR; - } - - public static final Descriptor DESCRIPTOR = new DescriptorImpl(); - protected static abstract class BaseJavadocAction implements Action { public String getUrlName() { return "javadoc"; @@ -197,6 +192,7 @@ public class JavadocArchiver extends Publisher { } } + @Extension public static class DescriptorImpl extends BuildStepDescriptor { public String getDisplayName() { return Messages.JavadocArchiver_DisplayName(); diff --git a/core/src/main/java/hudson/tasks/Mailer.java b/core/src/main/java/hudson/tasks/Mailer.java index 9612c58f05..0116eddac4 100644 --- a/core/src/main/java/hudson/tasks/Mailer.java +++ b/core/src/main/java/hudson/tasks/Mailer.java @@ -25,12 +25,11 @@ package hudson.tasks; import hudson.Launcher; import hudson.Functions; +import hudson.Extension; import hudson.maven.AbstractMavenProject; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; -import hudson.model.Build; import hudson.model.BuildListener; -import hudson.model.Descriptor; import hudson.model.User; import hudson.model.UserPropertyDescriptor; import hudson.util.FormFieldValidator; @@ -66,7 +65,7 @@ import net.sf.json.JSONObject; * * @author Kohsuke Kawaguchi */ -public class Mailer extends Publisher { +public class Mailer extends Notifier { protected static final Logger LOGGER = Logger.getLogger(Mailer.class.getName()); /** @@ -97,7 +96,7 @@ public class Mailer extends Publisher { /** Check whether a path (/-separated) will be archived. */ @Override public boolean artifactMatches(String path, AbstractBuild build) { - ArtifactArchiver aa = (ArtifactArchiver) build.getProject().getPublishersList().toMap().get(ArtifactArchiver.DESCRIPTOR); + ArtifactArchiver aa = build.getProject().getPublishersList().get(ArtifactArchiver.class); if (aa == null) { LOGGER.finer("No ArtifactArchiver found"); return false; @@ -119,12 +118,7 @@ public class Mailer extends Publisher { }.execute(build,listener); } - public Descriptor getDescriptor() { - return DESCRIPTOR; - } - - public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); - + @Extension public static final class DescriptorImpl extends BuildStepDescriptor { /** * The default e-mail address suffix appended to the user name found from changelog, diff --git a/core/src/main/java/hudson/tasks/Notifier.java b/core/src/main/java/hudson/tasks/Notifier.java new file mode 100644 index 0000000000..f1776d4398 --- /dev/null +++ b/core/src/main/java/hudson/tasks/Notifier.java @@ -0,0 +1,50 @@ +/* + * The MIT License + * + * Copyright (c) 2004-2009, Sun Microsystems, 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 hudson.tasks; + +import hudson.Extension; +import hudson.ExtensionPoint; + +/** + * {@link BuildStep}s that run after the build is completed. + * + *

+ * {@link Notifier} is a kind of {@link Publisher} that sends out the outcome of the builds to + * other systems and humans. This marking ensures that notifiers are run after the build result + * is set to its final values by other {@link Recorder}s. + * + *

+ * To register a custom {@link Publisher} from a plugin, + * put {@link Extension} on your descriptor. + * + * + * @author Kohsuke Kawaguchi + * @since 1.286 + * @see Recorder + */ +public abstract class Notifier extends Publisher implements ExtensionPoint { + public BuildStepDescriptor getDescriptor() { + return (BuildStepDescriptor)super.getDescriptor(); + } +} diff --git a/core/src/main/java/hudson/tasks/Publisher.java b/core/src/main/java/hudson/tasks/Publisher.java index 05554538d8..814dc3fd01 100644 --- a/core/src/main/java/hudson/tasks/Publisher.java +++ b/core/src/main/java/hudson/tasks/Publisher.java @@ -25,6 +25,8 @@ package hudson.tasks; import hudson.ExtensionPoint; import hudson.Launcher; +import hudson.DescriptorExtensionList; +import hudson.Extension; import hudson.maven.MavenReporter; import hudson.model.Action; import hudson.model.Build; @@ -32,13 +34,20 @@ import hudson.model.BuildListener; import hudson.model.Describable; import hudson.model.Project; import hudson.model.AbstractBuild; +import hudson.model.Descriptor; +import hudson.model.Hudson; + +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; /** * {@link BuildStep}s that run after the build is completed. * *

* To register a custom {@link Publisher} from a plugin, - * add it to {@link BuildStep#PUBLISHERS}. + * put {@link Extension} on your descriptor implementation. * *

* Starting 1.178, publishers are exposed to all kinds of different @@ -55,7 +64,15 @@ import hudson.model.AbstractBuild; * @author Kohsuke Kawaguchi */ public abstract class Publisher extends BuildStepCompatibilityLayer implements BuildStep, Describable, ExtensionPoint { -// + /** + * @deprecated + * Don't extend from {@link Publisher} directly. Instead, choose {@link Recorder} or {@link Notifier} + * as your base class. + */ + protected Publisher() { + } + + // // these two methods need to remain to keep binary compatibility with plugins built with Hudson < 1.150 // /** @@ -99,4 +116,55 @@ public abstract class Publisher extends BuildStepCompatibilityLayer implements B public boolean needsToRunAfterFinalized() { return false; } + + public Descriptor getDescriptor() { + return Hudson.getInstance().getDescriptor(getClass()); + } + + /** + * {@link Publisher} has a special sort semantics that requires a subtype. + * + * @see DescriptorExtensionList#create(Hudson, Class) + */ + public static final class DescriptorExtensionListImpl extends DescriptorExtensionList> + implements Comparator> { + public DescriptorExtensionListImpl(Hudson hudson) { + super(hudson,Publisher.class); + } + + @Override + protected List> sort(List> r) { + List> copy = new ArrayList>(r); + Collections.sort(copy,this); + return copy; + } + + public int compare(Descriptor lhs, Descriptor rhs) { + return classify(lhs)-classify(rhs); + } + + /** + * If recorder, return 0, if unknown return 1, if notifier returns 2. + * This is used as a sort key. + */ + private int classify(Descriptor d) { + if(Recorder.class.isAssignableFrom(d.clazz)) return 0; + if(Notifier.class.isAssignableFrom(d.clazz)) return 2; + + // for compatibility, if the descriptor is manually registered in a specific way, detect that. + Class kind = PublisherList.KIND.get(d); + if(kind==Recorder.class) return 0; + if(kind==Notifier.class) return 2; + + return 1; + } + } + + /** + * Returns all the registered {@link Publisher} descriptors. + */ + // for backward compatibility, the signature is not BuildStepDescriptor + public static DescriptorExtensionList> all() { + return Hudson.getInstance().getDescriptorList(Publisher.class); + } } diff --git a/core/src/main/java/hudson/tasks/Recorder.java b/core/src/main/java/hudson/tasks/Recorder.java new file mode 100644 index 0000000000..46b3a491dd --- /dev/null +++ b/core/src/main/java/hudson/tasks/Recorder.java @@ -0,0 +1,51 @@ +/* + * The MIT License + * + * Copyright (c) 2004-2009, Sun Microsystems, 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 hudson.tasks; + +import hudson.Extension; +import hudson.ExtensionPoint; + +/** + * {@link BuildStep}s that run after the build is completed. + * + *

+ * {@link Recorder} is a kind of {@link Publisher} that collects statistics from the build, + * and can mark builds as untable/failure. This marking ensures that builds are marked accordingly + * before notifications are sent via {@link Notifier}s. Otherwise, if the build is marked failed + * after some notifications are sent, inconsistency ensues. + * + *

+ * To register a custom {@link Publisher} from a plugin, + * put {@link Extension} on your descriptor. + * + * + * @author Kohsuke Kawaguchi + * @since 1.286 + * @see Notifier + */ +public abstract class Recorder extends Publisher implements ExtensionPoint { + public BuildStepDescriptor getDescriptor() { + return (BuildStepDescriptor)super.getDescriptor(); + } +} diff --git a/core/src/main/java/hudson/tasks/junit/JUnitResultArchiver.java b/core/src/main/java/hudson/tasks/junit/JUnitResultArchiver.java index dca1ac61f0..f926ec593a 100644 --- a/core/src/main/java/hudson/tasks/junit/JUnitResultArchiver.java +++ b/core/src/main/java/hudson/tasks/junit/JUnitResultArchiver.java @@ -26,6 +26,7 @@ package hudson.tasks.junit; import hudson.FilePath.FileCallable; import hudson.Launcher; import hudson.Util; +import hudson.Extension; import hudson.maven.AbstractMavenProject; import hudson.matrix.MatrixAggregatable; import hudson.matrix.MatrixAggregator; @@ -34,6 +35,7 @@ import hudson.model.*; import hudson.remoting.VirtualChannel; import hudson.tasks.Publisher; import hudson.tasks.BuildStepDescriptor; +import hudson.tasks.Recorder; import hudson.tasks.test.TestResultAggregator; import hudson.tasks.test.TestResultProjectAction; import hudson.util.FormFieldValidator; @@ -53,7 +55,7 @@ import java.io.Serializable; * * @author Kohsuke Kawaguchi */ -public class JUnitResultArchiver extends Publisher implements Serializable, MatrixAggregatable { +public class JUnitResultArchiver extends Recorder implements Serializable, MatrixAggregatable { /** * {@link FileSet} "includes" string, like "foo/bar/*.xml" @@ -130,15 +132,10 @@ public class JUnitResultArchiver extends Publisher implements Serializable, Matr return new TestResultAggregator(build,launcher,listener); } - public Descriptor getDescriptor() { - return DescriptorImpl.DESCRIPTOR; - } - private static final long serialVersionUID = 1L; + @Extension public static class DescriptorImpl extends BuildStepDescriptor { - public static final Descriptor DESCRIPTOR = new DescriptorImpl(); - public String getDisplayName() { return Messages.JUnitResultArchiver_DisplayName(); } diff --git a/core/src/main/java/hudson/tasks/test/AggregatedTestResultPublisher.java b/core/src/main/java/hudson/tasks/test/AggregatedTestResultPublisher.java index c7d077fcac..5de189a4f9 100644 --- a/core/src/main/java/hudson/tasks/test/AggregatedTestResultPublisher.java +++ b/core/src/main/java/hudson/tasks/test/AggregatedTestResultPublisher.java @@ -25,6 +25,7 @@ package hudson.tasks.test; import hudson.Launcher; import hudson.Util; +import hudson.Extension; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.BuildListener; @@ -37,6 +38,7 @@ import hudson.model.TaskListener; import hudson.model.listeners.RunListener; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.Publisher; +import hudson.tasks.Recorder; import hudson.util.FormFieldValidator; import net.sf.json.JSONObject; import org.kohsuke.stapler.QueryParameter; @@ -57,7 +59,7 @@ import java.util.List; * * @author Kohsuke Kawaguchi */ -public class AggregatedTestResultPublisher extends Publisher { +public class AggregatedTestResultPublisher extends Recorder { /** * Jobs to aggregate. Camma separated. * Null if triggering downstreams. @@ -74,10 +76,6 @@ public class AggregatedTestResultPublisher extends Publisher { return true; } - public DescriptorImpl getDescriptor() { - return DescriptorImpl.INSTANCE; - } - /** * Action that serves the aggregated record. * @@ -232,6 +230,7 @@ public class AggregatedTestResultPublisher extends Publisher { } } + @Extension public static final class DescriptorImpl extends BuildStepDescriptor { public boolean isApplicable(Class jobType) { return true; // for all types @@ -268,8 +267,6 @@ public class AggregatedTestResultPublisher extends Publisher { else return new AggregatedTestResultPublisher(s.getString("jobs")); } - - public static final DescriptorImpl INSTANCE = new DescriptorImpl(); } } diff --git a/test/src/main/java/org/jvnet/hudson/test/HudsonTestCase.java b/test/src/main/java/org/jvnet/hudson/test/HudsonTestCase.java index c8fc4651f5..9c0977cbf2 100644 --- a/test/src/main/java/org/jvnet/hudson/test/HudsonTestCase.java +++ b/test/src/main/java/org/jvnet/hudson/test/HudsonTestCase.java @@ -49,6 +49,7 @@ import hudson.slaves.RetentionStrategy; import hudson.tasks.BuildStep; import hudson.tasks.Mailer; import hudson.tasks.Maven; +import hudson.tasks.Publisher; import hudson.tasks.Maven.MavenInstallation; import hudson.util.ProcessTreeKiller; import hudson.util.StreamTaskListener; @@ -201,11 +202,6 @@ public abstract class HudsonTestCase extends TestCase { for (LenientRunnable r : tearDowns) r.run(); - // TODO: avoid relying on singletons and switch to some DI container. - // In the mean time, discard descriptors created during this exercise. - // without this, plugins loaded in the tests will be left and interferes with the later tests. - cleanUpDescriptors(BuildStep.PUBLISHERS); - hudson.cleanUp(); env.dispose(); } -- GitLab