Skip to content

Commit

Permalink
Send notification when build fails with error [Fixes #5]
Browse files Browse the repository at this point in the history
When a build fails (for example pom.xml malformed) the result
sent does not contain a project.
  • Loading branch information
jcgay committed May 11, 2014
1 parent ca9ceab commit 25dc105
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;

import java.util.List;
import java.util.concurrent.TimeUnit;

public abstract class AbstractCustomEventSpy implements Notifier {
Expand All @@ -31,6 +32,11 @@ public void close() {
// do nothing
}

@Override
public void onFailWithoutProject(List<Throwable> exceptions) {
// do nothing
}

@Override
public boolean shouldNotify() {
if (getClass().getName().contains(configuration.getImplementation())) {
Expand Down Expand Up @@ -111,4 +117,13 @@ protected String buildTitle(MavenExecutionResult result) {
}
return builder.toString();
}

protected String buildErrorDescription(List<Throwable> exceptions) {
StringBuilder builder = new StringBuilder();
for (Throwable exception : exceptions) {
builder.append(exception.getMessage());
builder.append(System.getProperty("line.separator"));
}
return builder.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,19 @@ public void init(Context context) throws Exception {

@Override
public void onEvent(Object event) throws Exception {
if (isExecutionResult(event) && shouldSendNotification()) {
activeNotifier.onEvent((MavenExecutionResult) event);
if (shouldSendNotification()) {
if (isExecutionResult(event) && hasFailedWithoutProject((MavenExecutionResult) event)) {
activeNotifier.onFailWithoutProject(((MavenExecutionResult) event).getExceptions());
} else if (isExecutionResult(event)) {
activeNotifier.onEvent((MavenExecutionResult) event);
}
}
}

private boolean hasFailedWithoutProject(MavenExecutionResult event) {
return event.getProject() == null && event.hasExceptions();
}

private boolean shouldSendNotification() {
return !"true".equalsIgnoreCase(System.getProperty(SKIP_NOTIFICATION));
}
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/com/github/jcgay/maven/notifier/Notifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import org.apache.maven.eventspy.EventSpy;
import org.apache.maven.execution.MavenExecutionResult;

import java.util.List;

public interface Notifier {

/**
Expand All @@ -29,4 +31,11 @@ public interface Notifier {
* Notifies the notifier of Maven's termination, allowing it to free any resources allocated by it.
*/
void close();

/**
* Notifies the notifier of a build ends with error without information about the build. <br />
* This is for example, a malformed {@code pom.xml} which breaks everything.
* @param exceptions
*/
void onFailWithoutProject(List<Throwable> exceptions);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.apache.maven.execution.MavenExecutionResult;
import org.codehaus.plexus.component.annotations.Component;

import java.util.List;
import java.util.concurrent.TimeUnit;

@Component(role = Notifier.class, hint = "growl")
Expand Down Expand Up @@ -50,6 +51,11 @@ public void close() {
}
}

@Override
public void onFailWithoutProject(List<Throwable> exceptions) {
sendMessageWithIcon(Status.FAILURE, "Build Error", buildErrorDescription(exceptions));
}

private void initGrowlClient() {
Gntp clientBuilder = Gntp.client(application)
.listener(new Slf4jGntpListener())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import org.apache.maven.execution.MavenExecutionResult;
import org.codehaus.plexus.component.annotations.Component;

import java.util.List;

@Component(role = Notifier.class, hint = "notification-center")
public class NotificationCenterNotifier extends AbstractCustomEventSpy {

Expand Down Expand Up @@ -38,25 +40,29 @@ public NotificationCenterNotifier() {
@Override
public void onEvent(MavenExecutionResult event) {
super.onEvent(event);
executor.exec(buildCommand(event));
executor.exec(buildCommand(getBuildStatus(event), event.getProject().getName(), buildNotificationMessage(event)));
}

@Override
public void onFailWithoutProject(List<Throwable> exceptions) {
executor.exec(buildCommand(Status.FAILURE, "Build Error", buildErrorDescription(exceptions)));
}

@Override
protected String buildNotificationMessage(MavenExecutionResult result) {
return buildShortDescription(result);
}

private String[] buildCommand(MavenExecutionResult result) {
Status status = getBuildStatus(result);
private String[] buildCommand(Status status, String name, String message) {

String[] commands = new String[configuration.getNotificationCenterSound() == null ? 13 : 15];
commands[0] = configuration.getNotificationCenterPath();
commands[1] = CMD_TITLE;
commands[2] = result.getProject().getName();
commands[2] = name;
commands[3] = CMD_SUBTITLE;
commands[4] = status.message();
commands[5] = CMD_MESSAGE;
commands[6] = buildNotificationMessage(result);
commands[6] = message;
commands[7] = CMD_GROUP;
commands[8] = GROUP;
commands[9] = CMD_ACTIVATE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
import com.github.jcgay.maven.notifier.AbstractCustomEventSpy;
import com.github.jcgay.maven.notifier.Configuration;
import com.github.jcgay.maven.notifier.Notifier;
import com.github.jcgay.maven.notifier.Status;
import com.github.jcgay.maven.notifier.executor.Executor;
import com.github.jcgay.maven.notifier.executor.RuntimeExecutor;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import org.apache.maven.execution.MavenExecutionResult;
import org.codehaus.plexus.component.annotations.Component;

import java.util.List;

@Component(role = Notifier.class, hint = "notify-send")
public class NotifySendNotifier extends AbstractCustomEventSpy {

Expand All @@ -31,18 +34,23 @@ public NotifySendNotifier() {
@Override
public void onEvent(MavenExecutionResult event) {
super.onEvent(event);
executor.exec(buildCommand(event));
executor.exec(buildCommand(buildTitle(event), buildNotificationMessage(event), getBuildStatus(event)));
}

@Override
public void onFailWithoutProject(List<Throwable> exceptions) {
executor.exec(buildCommand("Build Error", buildErrorDescription(exceptions), Status.FAILURE));
}

private String[] buildCommand(MavenExecutionResult result) {
private String[] buildCommand(String title, String message, Status status) {
String[] commands = new String[7];
commands[0] = configuration.getNotifySendPath();
commands[1] = buildTitle(result);
commands[2] = buildNotificationMessage(result);
commands[1] = title;
commands[2] = message;
commands[3] = CMD_TIMEOUT;
commands[4] = String.valueOf(configuration.getNotifySendTimeout());
commands[5] = CMD_ICON;
commands[6] = getBuildStatus(result).asPath();
commands[6] = status.asPath();

if (logger.isDebugEnabled()) {
logger.debug("Will execute command line: " + Joiner.on(" ").join(commands));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.github.jcgay.maven.notifier.snarl;

import com.github.jcgay.maven.notifier.AbstractCustomEventSpy;
import com.github.jcgay.maven.notifier.Status;
import com.github.jcgay.snp4j.Application;
import com.github.jcgay.snp4j.Icon;
import com.github.jcgay.snp4j.Server;
Expand All @@ -10,6 +11,8 @@
import org.apache.maven.execution.MavenExecutionResult;
import org.codehaus.plexus.component.annotations.Component;

import java.util.List;

import static com.google.common.io.Closeables.closeQuietly;

@Component(role = com.github.jcgay.maven.notifier.Notifier.class, hint = "snarl")
Expand All @@ -27,16 +30,24 @@ public void init(EventSpy.Context context) {
@Override
public void onEvent(MavenExecutionResult event) {
super.onEvent(event);
sendNotificationFor(getBuildStatus(event), buildNotificationMessage(event), buildTitle(event));
}

private void sendNotificationFor(Status status, String message, String title) {
Notification notification = new Notification();
notification.setIcon(Icon.base64(getBuildStatus(event).toByteArray()));
notification.setText(buildNotificationMessage(event));
notification.setTitle(buildTitle(event));
notification.setIcon(Icon.base64(status.toByteArray()));
notification.setText(message);
notification.setTitle(title);

try {
snarl.send(notification);
} finally {
closeQuietly(snarl);
}
}

@Override
public void onFailWithoutProject(List<Throwable> exceptions) {
sendNotificationFor(Status.FAILURE, buildErrorDescription(exceptions), "Build Error");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,30 @@
import javax.sound.sampled.UnsupportedAudioFileException;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

@Component(role = Notifier.class, hint = "sound")
public class SoundNotifier extends AbstractCustomEventSpy {

@Override
public void onEvent(MavenExecutionResult event) {
AudioInputStream ais = getAudioStream(getBuildStatus(event));
playSound(getBuildStatus(event));
}

private void playSound(Status status) {
AudioInputStream ais = getAudioStream(status);
if (ais == null) {
logger.warn("Cannot get a sound to play. Skipping notification...");
return;
}
play(ais);
}

@Override
public void onFailWithoutProject(List<Throwable> exceptions) {
playSound(Status.FAILURE);
}

private void play(AudioInputStream ais) {
try {
Clip clip = AudioSystem.getClip();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.awt.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

@Component(role = Notifier.class, hint = "system-tray")
public class SystemTrayNotifier extends AbstractCustomEventSpy {
Expand Down Expand Up @@ -64,6 +65,14 @@ public void close() {
}
}

@Override
public void onFailWithoutProject(List<Throwable> exceptions) {
if (!skipNotifications) {
icon.setImage(createImage(Status.FAILURE.toByteArray()));
icon.displayMessage("Build Error", buildErrorDescription(exceptions), toMessageType(Status.FAILURE));
}
}

private Image createImage(byte[] imageData) {
return Toolkit.getDefaultToolkit().createImage(imageData);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.jcgay.maven.notifier;

import org.apache.maven.execution.DefaultMavenExecutionResult;
import org.apache.maven.execution.MavenExecutionResult;
import org.mockito.InjectMocks;
import org.mockito.Mock;
Expand All @@ -9,6 +10,7 @@

import static com.github.jcgay.maven.notifier.NotificationEventSpyChooser.SKIP_NOTIFICATION;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;

Expand All @@ -28,6 +30,8 @@ public void setUp() throws Exception {
@Test
public void should_not_notify_if_event_is_not_a_build_result() throws Exception {

System.setProperty(SKIP_NOTIFICATION, String.valueOf(false));

chooser.onEvent("this is not a build result");

verifyZeroInteractions(notifier);
Expand All @@ -53,4 +57,18 @@ public void should_notify_when_property_skipNotification_is_false() throws Excep

verify(notifier).onEvent(event);
}

@Test
public void should_notify_failure_when_build_fails_without_project() throws Exception {

System.setProperty(SKIP_NOTIFICATION, String.valueOf(false));
DefaultMavenExecutionResult event = new DefaultMavenExecutionResult();
event.setProject(null);
event.addException(new NullPointerException());

chooser.onEvent(event);

verify(notifier).onFailWithoutProject(event.getExceptions());
verify(notifier, never()).onEvent(event);
}
}

0 comments on commit 25dc105

Please sign in to comment.