package org.hudson.trayapp;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.UIManager;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.hudson.trayapp.actions.Action;
import org.hudson.trayapp.gui.MainFrame;
import org.hudson.trayapp.gui.Tray;
import org.hudson.trayapp.gui.tray.TrayIconImplementation;
import org.hudson.trayapp.model.Job;
import org.hudson.trayapp.model.Model;
import org.hudson.trayapp.model.Preferences;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
public class HudsonTrayApp {
public final static String VERSION = "0.7.3";
public static void main(String[] args) {
HudsonTrayApp.args = args;
try {
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
}
getHudsonTrayAppInstance();
}
private static String[] args = null;
private static HudsonTrayApp hta = null;
public static HudsonTrayApp getHudsonTrayAppInstance() {
if (hta == null) {
hta = new HudsonTrayApp();
}
return hta;
}
private static String PERSISTENCEFILE = System.getProperty("user.home") + System.getProperty("file.separator") + ".hudson" + System.getProperty("file.separator") + "trayapp" + System.getProperty("file.separator") + "preferences";
private Model model = null;
private Preferences prefs = null;
private Tray tray = null;
private Timer timer = null;
public HudsonTrayApp() {
model = new Model();
prefs = new Preferences();
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputSource inputSource = new InputSource(new FileInputStream(PERSISTENCEFILE));
Document document = builder.parse(inputSource);
process(document.getChildNodes());
inputSource.getByteStream().close();
//
} catch (Exception e) {
e.printStackTrace();
}
String serverStart = getStartupServer();
if (serverStart != null && !model.containsServer(serverStart)) {
model.addServer(serverStart, "My Server");
}
MainFrame mainFrame = MainFrame.getMainFrameInstance();
mainFrame.setModel(model);
mainFrame.setPreferences(prefs);
tray = new Tray();
scheduleTimer(prefs.getUpdateFrequency() * 60000, true);
// Finally, we need to check that we have a server set up, if not, we'll ask the user to set one up
if (model.getServerModelSize() == 0) {
mainFrame.setVisible(true);
mainFrame.getMainTabbedPane().setSelectedIndex(1); // Switch to the Configuration Pane
}
}
public Model getModel() {
return model;
}
public Tray getTray() {
return tray;
}
public Preferences getPreferences() {
return prefs;
}
public void scheduleTimer(int interval, boolean now) {
if (timer != null) {
timer.cancel();
}
timer = new Timer(true);
timer.scheduleAtFixedRate(new UpdateTimerTask(), now ? 0l : (long) interval, interval);
}
public void update() {
model.update();
updateTrayIcon();
Integer colourCurrent = Job.convertColour(model.getWorstColour(false));
MainFrame.getMainFrameInstance().updateResults();
StringBuffer sb;
List worstJobs = model.getWorstJobs(false);
tray.rebuildPopupMenu(worstJobs);
// There is no point doing anything if we're on the first run.
if (model.getWorstJobs(true).isEmpty()) {
return;
}
boolean bldChgd = model.getBuildChanged();
Integer colourPrevious = Job.convertColour(model.getWorstColour(true));
Integer buildChanged = bldChgd ? Job.BUILD_CHANGED : Job.BUILD_UNCHANGED;
if (prefs.isShowPopupNotifications()) {
if (colourPrevious.intValue() > colourCurrent.intValue()) {
//List<Job>
tray.showMessage("Build Worsening", "The overall build status has degraded from\n"+Job.getColour(colourPrevious) + " to " + Job.getColour(colourCurrent), TrayIconImplementation.ERROR_MESSAGE_TYPE);
} else if (colourPrevious.intValue() < colourCurrent.intValue()) {
tray.showMessage("Build Improving", "The overall build status has improved from\n"+Job.getColour(colourPrevious) + " to " + Job.getColour(colourCurrent), TrayIconImplementation.INFO_MESSAGE_TYPE);
} else if (bldChgd && colourCurrent.intValue() < Job.BLUE.intValue()) {
List lstLeftJobs = model.getJobsLeftWorstBuild();
List lstJoinedJobs = model.getJobsJoinedWorstBuild();
if (lstLeftJobs.isEmpty() && lstJoinedJobs.isEmpty()) {
tray.showMessage("Build No Improvement", "The overall build has changed, but this has neither made an improvement or degredation.", TrayIconImplementation.INFO_MESSAGE_TYPE);
} else if (lstLeftJobs.isEmpty()) {
// We haven't had any jobs leave which means that some have joined our status.
sb = new StringBuffer();
for (int i = 0; i < lstJoinedJobs.size(); i++) {
sb.append("\n"+((Job) lstJoinedJobs.get(i)).getName());
}
tray.showMessage("Jobs have Joined Worst Colour", "The overall build worst colour hasn't changed, but the following projects have worsened, and joined this colour:" + sb.toString(), TrayIconImplementation.ERROR_MESSAGE_TYPE);
} else if (lstJoinedJobs.isEmpty()) {
// We haven't had any jobs join which means that some have left our status.
sb = new StringBuffer();
for (int i = 0; i < lstLeftJobs.size(); i++) {
sb.append("\n"+((Job) lstLeftJobs.get(i)).getName());
}
tray.showMessage("Jobs have left Worst Colour", "The overall build worst colour hasn't changed, but the following projects have improved away from this colour:" + sb.toString(), TrayIconImplementation.INFO_MESSAGE_TYPE);
} else {
// We had jobs join and leave our status.
StringBuffer sbleft = new StringBuffer();
for (int i = 0; i < lstLeftJobs.size(); i++) {
sbleft.append("\n"+((Job) lstLeftJobs.get(i)).getName());
}
StringBuffer sbjoin = new StringBuffer();
for (int i = 0; i < lstJoinedJobs.size(); i++) {
sbjoin.append("\n"+((Job) lstJoinedJobs.get(i)).getName());
}
tray.showMessage("Jobs have joined and left Worst Colour", "The overall build worst colour hasn't changed, but the following projects have worsened, and joined this colour:" + sbjoin.toString() + "\nThe following projects have improved away from this colour:" + sbleft.toString(), TrayIconImplementation.INFO_MESSAGE_TYPE);
}
}
}
// OK we've got the full status, let's fire the actions
Action action = prefs.getAction(colourPrevious, colourCurrent, buildChanged);
if (action != null) {
action.fireAction(colourPrevious, colourCurrent, buildChanged);
}
}
public void write() {
try {
File f = new File(PERSISTENCEFILE);
f.delete();
f.getParentFile().mkdirs();
f.createNewFile();
Writer w = new FileWriter(PERSISTENCEFILE);
writeXML(w);
w.close();
} catch (Exception e) {
TrayIconImplementation.displayException("File Write Exception", "Excpetion saving Preferences file", e);
}
}
private void writeXML(Writer w) throws IOException{
w.write("<hudsontrayapp>");
prefs.writeXML(w);
model.writeXML(w);
w.write("</hudsontrayapp>");
}
private void process(NodeList nodes) {
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
String name = node.getNodeName();
if (name.equals("hudsontrayapp")) {
process(node.getChildNodes());
} else if (name.equals("preferences")) {
prefs.process(node.getChildNodes());
} else if (name.equals("model")) {
model.process(node.getChildNodes());
}
}
}
private static String getStartupServer() {
for (int i = 0; i < args.length; i++) {
if (args[i].indexOf("server=") != -1) {
return args[i].substring("server=".length());
}
}
return null;
}
private class UpdateTimerTask extends TimerTask {
public void run() {
update();
}
}
public void updateTrayIcon() {
Integer colourCurrent = Job.convertColour(model.getWorstColour(false));
if (!prefs.isShowAnimatedBuilds()) {
if (colourCurrent == Job.BLUE_ANIME) {
colourCurrent = Job.BLUE;
} else if (colourCurrent == Job.YELLOW_ANIME) {
colourCurrent = Job.YELLOW;
} else if (colourCurrent == Job.RED_ANIME) {
colourCurrent = Job.RED;
}
// if (colourCurrent == Job.GREY_ANIME) {
// colourCurrent = Job.GREY;
// }
}
int health = prefs.isShowHealthIcon() ? model.getWorstJobsWorstHealth() : -1;
tray.setWorstCaseColour(Job.getColour(colourCurrent), health);
}
}