/** * Receives notifications about builds. * * <p>Listener is always Hudson-wide, so once registered it gets notifications for every build that * happens in this Hudson. * * <p>This is an abstract class so that methods added in the future won't break existing listeners. * * @author Kohsuke Kawaguchi * @since 1.145 */ public abstract class RunListener<R extends Run> implements ExtensionPoint { public final Class<R> targetType; protected RunListener(Class<R> targetType) { this.targetType = targetType; } protected RunListener() { Type type = Types.getBaseClass(getClass(), RunListener.class); if (type instanceof ParameterizedType) targetType = Types.erasure(Types.getTypeArgument(type, 0)); else throw new IllegalStateException(getClass() + " uses the raw type for extending RunListener"); } /** * Called after a build is completed. * * @param r The completed build. * @param listener The listener for this build. This can be used to produce log messages, for * example, which becomes a part of the "console output" of this build. But when this method * runs, the build is considered completed, so its status cannot be changed anymore. */ public void onCompleted(R r, TaskListener listener) {} /** * Called after a build is moved to the {@link Run.State#COMPLETED} state. * * <p>At this point, all the records related to a build is written down to the disk. As such, * {@link TaskListener} is no longer available. This happens later than {@link #onCompleted(Run, * TaskListener)}. */ public void onFinalized(R r) {} /** * Called when a build is started (i.e. it was in the queue, and will now start running on an * executor) * * @param r The started build. * @param listener The listener for this build. This can be used to produce log messages, for * example, which becomes a part of the "console output" of this build. */ public void onStarted(R r, TaskListener listener) {} /** * Called right before a build is going to be deleted. * * @param r The build. */ public void onDeleted(R r) {} /** * Registers this object as an active listener so that it can start getting callbacks invoked. * * @deprecated as of 1.281 Put {@link Extension} on your class to get it auto-registered. */ public void register() { all().add(this); } /** Reverse operation of {@link #register()}. */ public void unregister() { all().remove(this); } /** * List of registered listeners. * * @deprecated as of 1.281 Use {@link #all()} for read access, and use {@link Extension} for * registration. */ public static final CopyOnWriteList<RunListener> LISTENERS = ExtensionListView.createCopyOnWriteList(RunListener.class); /** Fires the {@link #onCompleted} event. */ public static void fireCompleted(Run r, TaskListener listener) { for (RunListener l : all()) { if (l.targetType.isInstance(r)) l.onCompleted(r, listener); } } /** Fires the {@link #onStarted} event. */ public static void fireStarted(Run r, TaskListener listener) { for (RunListener l : all()) { if (l.targetType.isInstance(r)) l.onStarted(r, listener); } } /** Fires the {@link #onFinalized(Run)} event. */ public static void fireFinalized(Run r) { for (RunListener l : all()) { if (l.targetType.isInstance(r)) l.onFinalized(r); } } /** Fires the {@link #onFinalized(Run)} event. */ public static void fireDeleted(Run r) { for (RunListener l : all()) { if (l.targetType.isInstance(r)) l.onDeleted(r); } } /** Returns all the registered {@link RunListener} descriptors. */ public static ExtensionList<RunListener> all() { return Hudson.getInstance().getExtensionList(RunListener.class); } }
/** * Infers e-mail addresses for the user when none is specified. * * <p>This is an extension point of Jenkins. Plugins that contribute a new implementation of this * class should put {@link Extension} on your implementation class, like this: * * <pre> * @Extension * class MyMailAddressResolver extends {@link MailAddressResolver} { * ... * } * </pre> * * <h2>Techniques</h2> * * <p>User identity in Jenkins is global, and not specific to a particular job. As a result, mail * address resolution only receives {@link User}, which by itself doesn't really have that much * information in it. * * <p>So the common technique for a mail address resolution is to define your own {@link * UserProperty} types and add it to {@link User} objects where more context is available. For * example, an {@link SCM} implementation can have a lot more information about a particular user * during a check out, so that would be a good place to capture information as {@link UserProperty}, * which then later used by a {@link MailAddressResolver}. * * @author Kohsuke Kawaguchi * @since 1.192 */ public abstract class MailAddressResolver implements ExtensionPoint { /** * Infers e-mail address of the given user. * * <p>This method is called when a {@link User} without explicitly configured e-mail address is * used, as an attempt to infer e-mail address. * * <p>The normal strategy is to look at {@link User#getProjects() the projects that the user is * participating}, then use the repository information to infer the e-mail address. * * <p>When multiple resolvers are installed, they are consulted in order and the search will be * over when an address is inferred by someone. * * <p>Since {@link MailAddressResolver} is singleton, this method can be invoked concurrently from * multiple threads. * * @return null if the inference failed. */ public abstract String findMailAddressFor(User u); /** * Try to resolve email address using resolvers. * * @return User address or null if resolution failed */ public static String resolve(User u) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Resolving e-mail address for \"" + u + "\" ID=" + u.getId()); } for (MailAddressResolver r : all()) { String email = r.findMailAddressFor(u); if (email != null) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine(r + " resolved " + u.getId() + " to " + email); } return email; } } // fall back logic return resolveFast(u); } /** * Try to resolve user email address fast enough to be used from UI * * <p>This implementation does not trigger {@link MailAddressResolver} extension point. * * @return User address or null if resolution failed */ public static String resolveFast(User u) { String extractedAddress = extractAddressFromId(u.getFullName()); if (extractedAddress != null) return extractedAddress; if (u.getFullName().contains("@")) // this already looks like an e-mail ID return u.getFullName(); String ds = Mailer.descriptor().getDefaultSuffix(); if (ds != null) { // another common pattern is "DOMAIN\person" in Windows. Only // do this when this full name is not manually set. see HUDSON-5164 Matcher m = WINDOWS_DOMAIN_REGEXP.matcher(u.getFullName()); if (m.matches() && u.getFullName().replace('\\', '_').equals(u.getId())) return m.group(1) + ds; // user+defaultSuffix return u.getId() + ds; } return null; } /** Tries to extract an email address from the user id, or returns null */ private static String extractAddressFromId(String id) { Matcher m = EMAIL_ADDRESS_REGEXP.matcher(id); if (m.matches()) return m.group(1); return null; } /** * Matches strings like "Kohsuke Kawaguchi <[email protected]>" * * @see #extractAddressFromId(String) */ private static final Pattern EMAIL_ADDRESS_REGEXP = Pattern.compile("^.*<([^>]+)>.*$"); /** Matches something like "DOMAIN\person" */ private static final Pattern WINDOWS_DOMAIN_REGEXP = Pattern.compile("[^\\\\ ]+\\\\([^\\\\ ]+)"); /** * All registered {@link MailAddressResolver} implementations. * * @deprecated as of 1.286 Use {@link #all()} for read access and {@link Extension} for * registration. */ public static final List<MailAddressResolver> LIST = ExtensionListView.createList(MailAddressResolver.class); /** Returns all the registered {@link MailAddressResolver} descriptors. */ public static ExtensionList<MailAddressResolver> all() { return Jenkins.getInstance().getExtensionList(MailAddressResolver.class); } private static final Logger LOGGER = Logger.getLogger(MailAddressResolver.class.getName()); }
/** * Performs mark up on changelog messages to be displayed. * * <p>SCM changelog messages are usually plain text, but when we display that in Hudson, it is often * nice to be able to put mark up on the text (for example to link to external issue tracking * system.) * * <p>Plugins that are interested in doing so may extend this class and put {@link Extension} on it. * When multiple annotators are registered, their results will be combined. * * @author Kohsuke Kawaguchi * @since 1.70 */ public abstract class ChangeLogAnnotator implements ExtensionPoint { /** * Called by Hudson to allow markups to be added to the changelog text. * * <p>This method is invoked each time a page is rendered, so implementations of this method * should not take too long to execute. Also note that this method may be invoked concurrently by * multiple threads. * * <p>If there's any error during the processing, it should be recorded in {@link Logger} and the * method should return normally. * * @param build Build that owns this changelog. From here you can access broader contextual * information, like the project, or it settings. Never null. * @param change The changelog entry for which this method is adding markup. Never null. * @param text The text and markups. Implementation of this method is expected to add additional * annotations into this object. If other annotators are registered, the object may already * contain some markups when this method is invoked. Never null. {@link MarkupText#getText()} * on this instance will return the same string as {@link Entry#getMsgEscaped()}. * @since 1.568 */ public void annotate(Run<?, ?> build, Entry change, MarkupText text) { if (build instanceof AbstractBuild && Util.isOverridden( ChangeLogAnnotator.class, getClass(), "annotate", AbstractBuild.class, Entry.class, MarkupText.class)) { annotate((AbstractBuild) build, change, text); } else { throw new AbstractMethodError("You must override the newer overload of annotate"); } } @Deprecated public void annotate(AbstractBuild<?, ?> build, Entry change, MarkupText text) { annotate((Run) build, change, text); } /** * Registers this annotator, so that Hudson starts using this object for adding markup. * * @deprecated as of 1.286 Prefer automatic registration via {@link Extension} */ @Deprecated public final void register() { all().add(this); } /** Unregisters this annotator, so that Hudson stops using this object. */ public final boolean unregister() { return all().remove(this); } /** * All registered {@link ChangeLogAnnotator}s. * * @deprecated as of 1.286 Use {@link #all()} for read access, and {@link Extension} for * registration. */ @Deprecated public static final CopyOnWriteList<ChangeLogAnnotator> annotators = ExtensionListView.createCopyOnWriteList(ChangeLogAnnotator.class); /** Returns all the registered {@link ChangeLogAnnotator} descriptors. */ public static ExtensionList<ChangeLogAnnotator> all() { return ExtensionList.lookup(ChangeLogAnnotator.class); } }