package hudson.plugins.sametime.im; import hudson.Launcher; import hudson.model.AbstractBuild; import hudson.model.BuildListener; import hudson.model.Hudson; import hudson.model.Result; import hudson.plugins.sametime.NotificationStrategy; import hudson.plugins.sametime.im.transport.SametimePublisherDescriptor; import hudson.plugins.sametime.tools.Assert; import hudson.scm.ChangeLogSet; import hudson.scm.ChangeLogSet.Entry; import hudson.tasks.BuildStepMonitor; import hudson.tasks.Notifier; import java.io.IOException; import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; /** * The actual Publisher that sends notification-Messages out to the clients. * @author Uwe Schaefer * */ public abstract class IMPublisher extends Notifier { private static final IMMessageTargetConverter CONVERTER = new DefaultIMMessageTargetConverter(); private final List<IMMessageTarget> targets = new LinkedList<IMMessageTarget>(); private final NotificationStrategy notificationStrategy; private final boolean notifyOnBuildStart; private final boolean notifySuspects; private final boolean notifyFixers; protected IMPublisher(final String targetsAsString, final String notificationStrategyString, final boolean notifyGroupChatsOnBuildStart, final boolean notifySuspects, final boolean notifyFixers) throws IMMessageTargetConversionException { Assert.isNotNull(targetsAsString, "Parameter 'targetsAsString' must not be null."); final String[] split = targetsAsString.split("\\s"); final IMMessageTargetConverter conv = getIMMessageTargetConverter(); for (final String fragment : split) { IMMessageTarget createIMMessageTarget; createIMMessageTarget = conv.fromString(fragment); if (createIMMessageTarget != null) { this.targets.add(createIMMessageTarget); } } if (notificationStrategyString == null) { this.notificationStrategy = NotificationStrategy.STATECHANGE_ONLY; } else if (notificationStrategyString.equals(SametimePublisherDescriptor.PARAMETERVALUE_STRATEGY_ALL)) { this.notificationStrategy = NotificationStrategy.ALL; } else if (notificationStrategyString.equals(SametimePublisherDescriptor.PARAMETERVALUE_STRATEGY_FAILURE)) { this.notificationStrategy = NotificationStrategy.ANY_FAILURE; } else { this.notificationStrategy = NotificationStrategy.STATECHANGE_ONLY; } this.notifyOnBuildStart = notifyGroupChatsOnBuildStart; this.notifySuspects = notifySuspects; this.notifyFixers = notifyFixers; } protected abstract IMConnection getIMConnection() throws IMException; protected IMMessageTargetConverter getIMMessageTargetConverter() { return IMPublisher.CONVERTER; } protected NotificationStrategy getNotificationStrategy() { return notificationStrategy; } private List<IMMessageTarget> getNotificationTargets() { return this.targets; } public final String getTargets() { final StringBuffer sb = new StringBuffer(); for (final IMMessageTarget t : this.targets) { if (t instanceof GroupChatIMMessageTarget && ! t.toString().contains("@conference.")) { sb.append("*"); } sb.append(t.toString()); sb.append(" "); } return sb.toString().trim(); } public final String getStrategy() { if (getNotificationStrategy() == NotificationStrategy.ALL) { return SametimePublisherDescriptor.PARAMETERVALUE_STRATEGY_ALL; } if (getNotificationStrategy() == NotificationStrategy.ANY_FAILURE) { return SametimePublisherDescriptor.PARAMETERVALUE_STRATEGY_FAILURE; } return SametimePublisherDescriptor.PARAMETERVALUE_STRATEGY_STATE_CHANGE; } public final boolean getNotifyOnStart() { return notifyOnBuildStart; } public final boolean getNotifySuspects() { return notifySuspects; } public final boolean getNotifyFixers() { return notifyFixers; } @Override public boolean perform(final AbstractBuild<?,?> build, final Launcher launcher, final BuildListener buildListener) throws InterruptedException, IOException { Assert.isNotNull(build, "Parameter 'build' must not be null."); Assert.isNotNull(buildListener, "Parameter 'arg2' must not be null."); if (getNotificationStrategy().notificationWanted(build)) { final StringBuffer sb = new StringBuffer(); sb.append("Project ").append(build.getProject().getName()) .append(" build (").append(build.getNumber()).append("): ") .append(build.getResult()).append(" in ") .append(build.getDurationString()) .append(": ") .append(Hudson.getInstance().getRootUrl()).append(build.getUrl()); if (build.getChangeSet() != null && ! build.getChangeSet().isEmptySet()) { boolean hasManyChangeSets = build.getChangeSet().getItems().length > 1; for (Entry entry : build.getChangeSet()) { sb.append("\n"); if (hasManyChangeSets) { sb.append("* "); } sb.append(entry.getAuthor()).append(": ").append(entry.getMsg()); } } final String msg = sb.toString(); for (final IMMessageTarget target : getNotificationTargets()) { try { buildListener.getLogger().append("Sending notification to: " + target.toString() + "\n"); getIMConnection().send(target, msg); } catch (final Throwable e) { buildListener.getLogger().append("There was an Error sending notification to: " + target.toString() + "\n"); } } } if (this.notifySuspects && build.getResult().isWorseThan(Result.SUCCESS)) { final String message = new StringBuffer("You're suspected of having broken ") .append(build.getProject().getName()).append(": ") .append(Hudson.getInstance().getRootUrl()).append(build.getUrl()) .toString(); for (final IMMessageTarget target : calculateSuspectsTargets(build.getChangeSet())) { try { getIMConnection().send(target, message); } catch (final Throwable e) { buildListener.getLogger().append("There was an Error sending suspect notification to: " + target.toString() + "\n"); } } } if (this.notifyFixers && build.getResult() == Result.SUCCESS && build.getPreviousBuild() != null && build.getPreviousBuild().getResult().isWorseThan(Result.SUCCESS)) { final String message = new StringBuffer("Seems you've fixed ") .append(build.getProject().getName()).append(": ") .append(Hudson.getInstance().getRootUrl()).append(build.getUrl()) .toString(); for (final IMMessageTarget target : calculateSuspectsTargets(build.getChangeSet())) { try { getIMConnection().send(target, message); } catch (final Throwable e) { buildListener.getLogger().append("There was an Error sending fixer notification to: " + target.toString() + "\n"); } } } return true; } /* (non-Javadoc) * @see hudson.tasks.Publisher#prebuild(hudson.model.AbstractBuild, hudson.model.BuildListener) */ @Override public boolean prebuild(AbstractBuild build, BuildListener buildListener) { try { if (notifyOnBuildStart) { final StringBuffer sb = new StringBuffer("Starting build ").append(build.getNumber()) .append(" for job ").append(build.getProject().getName()); if (build.getPreviousBuild() != null) { sb.append(" (previous build: ").append(build.getPreviousBuild().getResult().toString().toLowerCase()); if (build.getPreviousBuild().getResult().isWorseThan(Result.SUCCESS)) { sb.append(" -- last ").append(build.getPreviousNotFailedBuild().getResult().toString().toLowerCase()) .append(" #").append(build.getPreviousNotFailedBuild().getNumber()) .append(" ").append(build.getPreviousNotFailedBuild().getTimestampString()).append(" ago"); } sb.append(")"); } final String msg = sb.toString(); for (final IMMessageTarget target : getNotificationTargets()) { // only notify group chats if (target instanceof GroupChatIMMessageTarget) { try { getIMConnection().send(target, msg); } catch (final Throwable e) { buildListener.getLogger().append("There was an Error sending notification to: " + target.toString() + "\n"); } } } } } catch (Throwable t) { // ignore: never, ever cancel a build because a notification fails } return true; } private Collection<IMMessageTarget> calculateSuspectsTargets(ChangeLogSet<? extends Entry> changeLogSet) { Set<IMMessageTarget> suspects = new HashSet<IMMessageTarget>(); if (changeLogSet != null && ! changeLogSet.isEmptySet()) { for (Entry e : changeLogSet) { String userId = e.getAuthor().getId(); if (userId != null && userId.length() > 0) { try { suspects.add(CONVERTER.fromString(userId)); } catch (final IMMessageTargetConversionException dontCare) { } } } } return suspects; } @Override public boolean needsToRunAfterFinalized() { return true; } public BuildStepMonitor getRequiredMonitorService() { return BuildStepMonitor.BUILD; } }