Beispiel #1
0
/**
 * 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>
 * &#64;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 &lt;[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);
  }
}