Commits

Anonymous committed 1946f3d

Initial commit.

  • Participants

Comments (0)

Files changed (12)

+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src/java"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="output" path="build/classes"/>
+</classpath>
+syntax: glob
+*.class
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>BuildBotIcon</name>
+	<comment></comment>
+	<projects>
+		<project>_work</project>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
+* Copyright (c) 2009, Marcus Lindblom
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of the <organization> nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY <copyright holder> ''AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+<project name="BuildBotIcon" default="all">
+                
+	<property name="build.dir" location="${basedir}/build"/>
+	   
+	<target name="setup">
+	   <mkdir dir="${build.dir}"/>
+	</target>
+	
+	<target name="all" depends="build, doc"/>
+
+	<target name="build" depends="jar"/>
+	
+    <target name="compile" depends="setup">
+        <javac srcdir="src/java" destdir="${build.dir}/classes"/>
+    </target>
+   
+    <target name="clean" depends="setup">
+        <delete dir="${build.dir}"/>
+    </target>
+    
+    <target name="jar" depends="compile">
+        <jar jarfile="${build.dir}/BuildbotIcon.jar" basedir="${build.dir}/classes" />                    
+        <jar jarfile="${build.dir}/BuildbotIcon.jar" manifest="scripts/manifest.MF" 
+             basedir="src/java" update="true" />                    
+    </target>
+        
+    <target name="doc" depends="setup">
+	    <mkdir dir="${build.dir}/doc"/>
+        <javadoc packagenames="*" 
+                 access="private"
+                 destdir="${build.dir}/doc"
+                 use="true"
+                 author="false"
+                 version="false"
+                 linksource="yes"
+                 windowtitle="BuildBotIcon Java API"
+                 sourcepath="src/java" />
+     </target>
+                   
+</project>

File scripts/Manifest.MF

+Manifest-Version: 1.0
+Sealed: true
+Main-Class: net.buildbot.BuildBotIcon

File src/java/net/buildbot/BuildBotIcon.java

+/*
+* Copyright (c) 2009, Marcus Lindblom
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of the <organization> nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY <copyright holder> ''AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package net.buildbot;
+
+import java.awt.Image;
+import java.awt.MenuItem;
+import java.awt.PopupMenu;
+import java.awt.SystemTray;
+import java.awt.Toolkit;
+import java.awt.TrayIcon;
+import java.awt.TrayIcon.MessageType;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.swing.JOptionPane;
+import javax.swing.UIManager;
+
+public class BuildBotIcon implements ActionListener {
+
+    private static final int SLEEP_TIME = 60 * 1000;
+    private static Image s_errorImage;
+    private static Image s_successImage;
+    private static Image s_failImage;
+    private static Image s_warnImage;
+    private static Image s_exceptionImage;
+    private static Matcher s_matcher = Pattern.compile("(success|warn|fail|exception)",
+            Pattern.MULTILINE | Pattern.CASE_INSENSITIVE).matcher("");
+
+    TrayIcon trayIcon = null;
+    private PopupMenu popup;
+    private MenuItem exitItem;
+    private MenuItem aboutItem;
+    private Image m_current = null;
+    private URL url;
+    private String toolTip;
+    private static List<BuildBotIcon> s_icons;
+
+    enum Status {
+        SUCCESS, WARN, FAIL, EXCEPTION, CONNECT_ERROR
+    }
+
+    static private void loadResources() throws IOException {
+        s_errorImage = loadImage("buildbot.gif");
+        s_successImage = loadImage("buildbot-good.gif");
+        s_failImage = loadImage("buildbot-bad.gif");
+        s_warnImage = loadImage("buildbot-warn.gif");
+        s_exceptionImage = loadImage("buildbot-exception.gif");
+    }
+
+    public BuildBotIcon(URL url) throws Exception {
+        //System.out.println("Creating for " + url);
+
+        this.url = url;
+        popup = new PopupMenu();
+        exitItem = new MenuItem("Exit");
+        aboutItem = new MenuItem("About");
+        popup.add(aboutItem);
+        popup.add(exitItem);
+
+        exitItem.addActionListener(this);
+        aboutItem.addActionListener(this);
+
+        toolTip = "Listening to " + url;
+
+        trayIcon = new TrayIcon(s_errorImage, toolTip);
+        trayIcon.setPopupMenu(popup);
+
+        SystemTray tray = SystemTray.getSystemTray();
+        tray.add(trayIcon);
+    }
+
+    private void update(boolean didSleep) {
+        Image img;
+        String caption;
+        String text;
+        String toolTipSuffix;
+        MessageType type;
+
+        trayIcon.setImage(s_errorImage);
+
+        try {
+            switch (getBuildStatus()) {
+            case SUCCESS:
+                img = s_successImage;
+                caption = "Build ok.";
+                toolTipSuffix = "ok";
+                text = "A build at " + url + " has completed sucessfully.";
+                type = MessageType.INFO;
+                break;
+            case WARN:
+                img = s_warnImage;
+                caption = "Build has warnings!";
+                text = "A build at " + url + " has completed with warnings.";
+                toolTipSuffix = "warnings";
+                type = MessageType.WARNING;
+                break;
+            case FAIL:
+                img = s_failImage;
+                caption = "Build failed!";
+                text = "A build at " + url + " has failed.";
+                toolTipSuffix = "failed";
+                type = MessageType.WARNING;
+                break;
+            case EXCEPTION:
+                img = s_exceptionImage;
+                caption = "Build failed with exception!";
+                text = "A build at " + url + " has failed with an exception.";
+                toolTipSuffix = "exception";
+                type = MessageType.WARNING;
+                break;
+            case CONNECT_ERROR:
+                throw new IOException("Unknown connect error");
+            default:
+                throw new Error("Unknown error");
+            }
+
+        } catch (Exception e) {
+            img = s_errorImage;
+            caption = "Error: " + e.getClass();
+            text = e.getMessage() + " at " + url;
+            toolTipSuffix = "problem";
+            type = MessageType.ERROR;
+        }
+
+        if (m_current != img) {
+            if (!didSleep) {
+                trayIcon.displayMessage(caption, text, type);
+            }
+            m_current = img;
+        }
+        trayIcon.setToolTip(toolTip + " (" + toolTipSuffix + ")" + " at "
+                + new SimpleDateFormat("HH:mm:ss").format(new Date()));
+        trayIcon.setImage(img);
+    }
+
+    private Status getBuildStatus() throws Exception {
+        BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
+        String l;
+        Status status = null;
+
+        while ((l = in.readLine()) != null) {
+            s_matcher.reset(l);
+
+            while (s_matcher.find()) {
+                String s = s_matcher.group(1);
+
+                for (Status st : Status.values()) {
+                    if ((status == null || st.ordinal() > status.ordinal()) && s.equalsIgnoreCase(st.name())) {
+                        status = st;
+                    }
+                }
+            }
+        }
+
+        in.close();
+
+        if (status == null) {
+            throw new Exception("Unable to read status from page contents");
+        }
+
+        return status;
+    }
+
+    private static Image loadImage(String file) throws IOException {
+        Image img = Toolkit.getDefaultToolkit().getImage(BuildBotIcon.class.getResource(file));
+        if (img == null) {
+            throw new IOException("Unable to find image: " + file);
+        }
+        return img;
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        MenuItem mi = (MenuItem) e.getSource();
+        if (mi.getLabel() == "Exit") {
+            System.exit(0);
+        } else if (mi.getLabel() == "About") {
+            StringBuilder sb = new StringBuilder();
+            sb.append("BuildBotIcon - Copyright Marcus Lindblom 2009 - BSD License\n");
+
+            for (BuildBotIcon i : s_icons) {
+                sb.append('\n');
+                sb.append(i.trayIcon.getToolTip());
+            }
+
+            JOptionPane.showMessageDialog(null, sb.toString());
+        }
+    }
+
+    public static void main(String args[]) {
+        try {
+            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+
+            if (args.length == 0) {
+                JOptionPane.showMessageDialog(null, "Usage: buildboticon <url1> <url2> <...>\n\n"
+                        + "Example 'BuildBotIcon.jar http://myserver:8000/one_box_per_builder?builder=my-incremental-build'",
+                        "Usage", JOptionPane.INFORMATION_MESSAGE);
+            } else {
+                loadResources();
+                mainloop(args);
+            }
+        } catch (Exception e) {
+            JOptionPane.showMessageDialog(null, e.getClass() + "\n" + e.getMessage(), "Error",
+                    JOptionPane.ERROR_MESSAGE);
+        }
+    }
+
+    private static void mainloop(String[] urls) throws Exception {
+        s_icons = new LinkedList<BuildBotIcon>();
+
+        for (String url : urls) {
+            s_icons.add(new BuildBotIcon(new URL(url)));
+        }
+
+        Date time = null;
+
+        while (true) {
+            Date newTime = new Date();
+            boolean didSleep = time == null || newTime.getTime() - time.getTime() > SLEEP_TIME * 3;
+            time = newTime;
+
+            for (BuildBotIcon bi : s_icons) {
+                bi.update(didSleep);
+            }
+
+            try {
+                Thread.sleep(SLEEP_TIME);
+            } catch (InterruptedException e1) {
+                e1.printStackTrace();
+                return;
+            }
+
+        }
+    }
+}

File src/java/net/buildbot/buildbot-bad.gif

Added
New image

File src/java/net/buildbot/buildbot-exception.gif

Added
New image

File src/java/net/buildbot/buildbot-good.gif

Added
New image

File src/java/net/buildbot/buildbot-warn.gif

Added
New image

File src/java/net/buildbot/buildbot.gif

Added
New image