Commits

Colin Gordon committed aa85f4e

adding mostly finished project

  • Participants
  • Parent commits 2bc0284

Comments (0)

Files changed (70)

+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry exported="true" kind="lib" path="lib/apache-log4j.jar"/>
+	<classpathentry exported="true" kind="lib" path="lib/commons-codec-1.3.jar"/>
+	<classpathentry exported="true" kind="lib" path="lib/commons-logging-1.0.4.jar"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="lib" path="lib/commons-httpclient-3.1.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>hudson-eclipse</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>

File .settings/org.eclipse.jdt.core.prefs

+#Thu Oct 15 10:38:55 CEST 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5

File META-INF/MANIFEST.MF

+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Hudson Plug-in for Eclipse
+Bundle-SymbolicName: dk.contix.eclipse.hudson;singleton:=true
+Bundle-Version: 1.0.10
+Bundle-Activator: dk.contix.eclipse.hudson.Activator
+Bundle-Vendor: Contix Technologies
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.ui.forms,
+ org.eclipse.ui.workbench.texteditor,
+ org.eclipse.core.net
+Eclipse-LazyStart: true
+Bundle-ClassPath: .,
+ lib/commons-httpclient-3.1.jar,
+ lib/commons-logging-1.0.4.jar,
+ lib/commons-codec-1.3.jar,
+ lib/apache-log4j.jar
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6

File bin/log4j.properties

+log4j.rootCategory=info, dest1
+
+# log to console
+log4j.appender.dest1=org.apache.log4j.ConsoleAppender
+log4j.appender.dest1.ImmediateFlush=true
+log4j.appender.dest1.layout=org.apache.log4j.PatternLayout
+log4j.appender.dest1.layout.ConversionPattern=%d [%-2p] %c - %m%n
+
+log4j.logger.dk=info, dest1
+

File build.properties

+source.. = src/
+output.. = bin/
+bin.includes = plugin.xml,\
+               META-INF/,\
+               .,\
+               icons/,\
+               lib/commons-httpclient-3.1.jar,\
+               lib/commons-logging-1.0.4.jar,\
+               lib/commons-codec-1.3.jar,\
+               lib/apache-log4j.jar
+jars.compile.order = .

File icons/blue.png

Added
New image

File icons/blue_anime.png

Added
New image

File icons/disabled.png

Added
New image

File icons/disabled_anime.png

Added
New image

File icons/grey.png

Added
New image

File icons/grey_anime.png

Added
New image

File icons/health_0.png

Added
New image

File icons/health_20.png

Added
New image

File icons/health_40.png

Added
New image

File icons/health_60.png

Added
New image

File icons/health_80.png

Added
New image

File icons/hudson.png

Added
New image

File icons/red.png

Added
New image

File icons/red_anime.png

Added
New image

File icons/refresh.png

Added
New image

File icons/schedule.png

Added
New image

File icons/unknown.png

Added
New image

File icons/unknown_anime.png

Added
New image

File icons/yellow.png

Added
New image

File icons/yellow_anime.png

Added
New image

File lib/apache-log4j.jar

Binary file added.

File lib/commons-codec-1.3.jar

Binary file added.

File lib/commons-httpclient-3.1.jar

Binary file added.

File lib/commons-logging-1.0.4.jar

Binary file added.
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.2"?>
+<plugin>
+
+   <extension
+         point="org.eclipse.ui.views">
+      <category
+            name="Hudson"
+            id="dk.contix.eclipse.hudson">
+      </category>
+      <view
+            name="Hudson"
+            icon="icons/hudson.png"
+            category="dk.contix.eclipse.hudson"
+            class="dk.contix.eclipse.hudson.views.HudsonView"
+            id="dk.contix.eclipse.hudson.views.HudsonView">
+      </view>
+   </extension>
+   <extension
+         point="org.eclipse.ui.perspectiveExtensions">
+      <perspectiveExtension
+            targetID="org.eclipse.ui.resourcePerspective">
+         <view
+               ratio="0.5"
+               relative="org.eclipse.ui.views.TaskList"
+               relationship="stack"
+               id="dk.contix.eclipse.hudson.views.HudsonView">
+         </view>
+      </perspectiveExtension>
+   </extension>
+   <extension
+         point="org.eclipse.ui.preferencePages">
+      <page
+            class="dk.contix.eclipse.hudson.preference.HudsonPreferencesPage"
+            id="dk.contix.eclipse.hudson.preference"
+            name="Hudson"/>
+   </extension>
+   <extension
+         point="org.eclipse.core.runtime.preferences">
+      <initializer class="dk.contix.eclipse.hudson.preference.PreferenceInitializer"/>
+   </extension>
+   <extension
+         point="org.eclipse.ui.editors">
+      <editor
+            class="dk.contix.eclipse.hudson.views.HudsonBrowser"
+            default="false"
+            icon="icons/hudson.png"
+            id="dk.contix.eclipse.hudson.browser"
+            name="Browser"/>
+   </extension>
+
+</plugin>

File src/analyze.sh

+#!/bin/bash
+export PATH=~/research/guitypes/checker/binary:$PATH
+
+CHECKER=/homes/gws/csgordon/research/guitypes/checker/dist/lib/guitypes-`date "+%Y%m%d"`.jar
+
+CFJARS="/homes/gws/csgordon/research/guitypes/checker/binary/jsr308-all.jar"
+
+JARS="/homes/gws/csgordon/research/experiments/hudson-eclipse/lib/commons-logging-1.0.4.jar:/homes/gws/csgordon/research/experiments/hudson-eclipse/lib/commons-httpclient-3.1.jar:/homes/gws/csgordon/research/experiments/hudson-eclipse/lib/apache-log4j.jar:/homes/gws/csgordon/research/experiments/hudson-eclipse/lib/commons-codec-1.3.jar"
+
+ECLIPSEJARS="/usr/lib/eclipse/plugins/org.eclipse.swt.gtk.linux.x86_64_3.7.0.dist.jar:/usr/lib/eclipse/plugins/org.eclipse.ui_3.7.0.dist.jar:/usr/lib/eclipse/plugins/org.eclipse.swt_3.7.0.dist.jar:/usr/lib/eclipse/plugins/org.eclipse.jface_3.7.0.dist.jar:/usr/lib/eclipse/plugins/org.eclipse.core.commands_3.6.0.dist.jar:/usr/lib/eclipse/plugins/org.eclipse.ui.workbench_3.7.0.dist.jar:/usr/lib/eclipse/plugins/org.eclipse.core.runtime_3.7.0.dist.jar:/usr/lib/eclipse/plugins/org.eclipse.osgi_3.7.0.dist.jar:/usr/lib/eclipse/plugins/org.eclipse.equinox.common_3.6.0.dist.jar:/usr/lib/eclipse/plugins/org.eclipse.core.jobs_3.5.100.dist.jar:/usr/lib/ecliplse/plugins/org.eclipse.core.runtime.compatibility.registry_3.5.0.dist/runtime_registry_compatibility.jar:/usr/lib/eclipse/plugins/org.eclipse.equinox.registry_3.5.100.dist.jar:/usr/lib/eclipse/plugins/org.eclipse.equinox.preferences_3.4.0.dist.jar:/usr/lib/eclipse/plugins/org.eclipse.core.contenttype_3.4.100.dist.jar:/usr/lib/eclipse/plugins/org.eclipse.equinox.app_1.3.100.dist.jar:/usr/lib/eclipse/plugins/org.eclipse.ui.forms_3.5.100.dist.jar:/usr/lib/eclipse/plugins/org.eclipse.ui.workbench.texteditor_3.7.0.dist.jar:/usr/lib/eclipse/plugins/org.eclipse.core.net_1.2.100.dist.jar"
+
+
+
+DEBUG= #"-AprintErrorStack -Afilenames -Ashowchecks" #-Alint=debugSpew"
+ERRS=999
+
+#COMMAND="javac -J-Xbootclasspath/p:/homes/gws/csgordon/research/guitypes/checker/binary/jsr308-all.jar -J-Xms48m -J-cp -J$CFJARS -Xmaxerrs 999 -cp $CHECKER:$CFJARS:$JARS -processor guitypes.checkers.GUIEffectsChecker $DEBUG"
+#COMMAND="javac -J-Xbootclasspath/p:/homes/gws/csgordon/research/guitypes/checker/binary/jsr308-all.jar -Xmaxerrs 999 -cp $CHECKER:$JARS -processor guitypes.checkers.GUIEffectsChecker $DEBUG"
+#COMMAND="javac -Xmaxerrs 999 -cp $CHECKER:$JARS -processor guitypes.checkers.GUIEffectsChecker $DEBUG"
+COMMAND="javac -J-Xbootclasspath/p:$CFJARS -Xmaxerrs $ERRS -cp $CHECKER:$JARS:$ECLIPSEJARS -processor guitypes.checkers.GUIEffectsChecker $DEBUG"
+
+echo $COMMAND
+
+find . -name '*.java' | xargs $COMMAND
+
+
+#

File src/dk/contix/eclipse/hudson/Activator.java

+package dk.contix.eclipse.hudson;
+
+import org.eclipse.core.net.proxy.IProxyService;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+import guitypes.checkers.quals.*;
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends AbstractUIPlugin {
+
+	// The plug-in ID
+	public static final String PLUGIN_ID = "dk.contix.eclipse.hudson";
+
+	public static final String JOB_FAMILY_UPDATE = PLUGIN_ID + ".update";
+
+	public static final String PREF_BASE_URL = "base_url";
+
+	public static final String PREF_AUTO_UPDATE = "auto_update";
+
+	public static final String PREF_UPDATE_INTERVAL = "update_interval";
+
+	public static final String PREF_POPUP_ON_ERROR = "popup_error";
+	
+	public static final String PREF_POPUP_ON_CONNECTION_ERROR = "popup_connection_error";
+
+	public static final String PREF_FILTER_SUCCESS = "filter_success";
+
+	public static final String PREF_FILTER_FAIL = "filter_fail";
+
+	public static final String PREF_FILTER_FAIL_TEST = "filter_fail_test";
+
+	public static final String PREF_FILTER_NO_BUILD = "filter_no_build";
+
+	public static final String PREF_FILTER_IGNORE_PROJECT = "filter_ignore_build";
+
+	public static final String PREF_FILTER_DISABLED = "filter_disabled";
+
+	public static final String PREF_SECURITY_TOKEN = "security_token";
+	
+	public static final String PREF_FILTER_NAME = "filter_by_name";
+	
+	public static final String PREF_SELECTED_VIEW = "selected_view";
+
+	public static final String PREF_USE_AUTH = "use_auth";
+
+	public static final String PREF_LOGIN = "login";
+
+	public static final String PREF_PASSWORD = "password";
+	
+	public static final String PREF_PARAMETERS = "parameter_";
+
+	// The shared instance
+	private static Activator plugin;
+
+	private ImageRegistry registry;
+
+	private ServiceTracker tracker;
+
+	/**
+	 * The constructor
+	 */
+	public Activator() {
+		plugin = this;
+		this.registry = new ImageRegistry();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+	 */
+	public void start(BundleContext context) throws Exception {
+		super.start(context);
+
+		tracker = new ServiceTracker(getBundle().getBundleContext(), IProxyService.class.getName(), null);
+		tracker.open();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+	 */
+	public void stop(BundleContext context) throws Exception {
+		plugin = null;
+		tracker.close();
+		super.stop(context);
+	}
+
+	/**
+	 * Returns the shared instance
+	 * 
+	 * @return the shared instance
+	 */
+	public static Activator getDefault() {
+		return plugin;
+	}
+
+	/**
+	 * Returns an image descriptor for the image file at the given plug-in relative path
+	 * 
+	 * @param path
+	 *            the path
+	 * @return the image descriptor
+	 */
+	public static ImageDescriptor getImageDescriptor(String path) {
+		return imageDescriptorFromPlugin(PLUGIN_ID, path);
+	}
+
+	/**
+	 * Returns an image for the image file at the given plug-in relative path
+	 * 
+	 * @param path
+	 *            the path
+	 * @return the image
+	 */
+
+	@UIEffect public static Image getImage(String path) {
+		Image img = plugin.registry.get(path);
+		if (img == null) {
+			final ImageDescriptor desc = getImageDescriptor(path);
+			if (desc != null) {
+				img = desc.createImage(true);
+				plugin.registry.put(path, img);
+			}
+		}
+		return img;
+	}
+
+	public IProxyService getProxyService() {
+		return (IProxyService) tracker.getService();
+	}
+
+}

File src/dk/contix/eclipse/hudson/Build.java

+package dk.contix.eclipse.hudson;
+
+import java.sql.Timestamp;
+import java.util.List;
+
+/**
+ * Representation of a Hudson build.
+ *
+ * @author Andre Bossert
+ *
+ */
+public class Build {
+
+	private String number;
+	private String url;
+	private String status;
+	private String id;
+	private long timestamp;
+	private List<BuildParameter> parameters;
+
+	public Build(String number, String url, String status, String id, long timestamp, List<BuildParameter> parameters) {
+		super();
+		this.number = number;
+		this.url = url;
+		this.status = status;
+		this.id = id;
+		this.timestamp = timestamp;
+		this.parameters = parameters;
+	}
+
+	public String getNumber() {
+		return number;
+	}
+
+	public String getUrl() {
+		return url;
+	}
+
+	public String getId() {
+		return id;
+	}
+
+	public long getTimestampLong() {
+		return timestamp;
+	}
+
+	public Timestamp getTimestamp() {
+		return new Timestamp(timestamp);
+	}
+
+	public String getStatus() {
+		return status;
+	}
+
+	public List<BuildParameter> getParameters() {
+		return parameters;
+	}
+
+	public int hashCode() {
+		final int PRIME = 31;
+		int result = 1;
+		result = PRIME * result + ((number == null) ? 0 : number.hashCode());
+		result = PRIME * result + ((url == null) ? 0 : url.hashCode());
+		result = PRIME * result + ((status == null) ? 0 : status.hashCode());
+		result = PRIME * result + ((id == null) ? 0 : id.hashCode());
+		return result;
+	}
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		final Build other = (Build)obj;
+		if (number == null) {
+			if (other.number != null)
+				return false;
+		} else if (!number.equals(other.number))
+			return false;
+		if (url == null) {
+			if (other.url != null)
+				return false;
+		} else if (!url.equals(other.url))
+			return false;
+		if (status == null) {
+			if (other.status != null)
+				return false;
+		} else if (!status.equals(other.status))
+			return false;
+		if (id == null) {
+			if (other.id != null)
+				return false;
+		} else if (!id.equals(other.id))
+			return false;
+		if (timestamp == 0) {
+			if (other.timestamp != 0)
+				return false;
+		} else if (timestamp == other.timestamp)
+			return false;
+		return true;
+	}
+}

File src/dk/contix/eclipse/hudson/BuildHealth.java

+package dk.contix.eclipse.hudson;
+
+import java.util.List;
+
+import org.eclipse.swt.graphics.Image;
+import guitypes.checkers.quals.*;
+
+public class BuildHealth implements Comparable<BuildHealth> {
+
+	private final int health;
+
+	public BuildHealth(int health) {
+		this.health = health - (health % 20);
+	}
+	
+	public static BuildHealth getLowest(List<String> values) {
+		BuildHealth last = null;
+		for (String val : values) {
+			BuildHealth h = new BuildHealth(Integer.parseInt(val));
+			if (last == null) {
+				last = h;
+			} else if (last.compareTo(h) > 0) {
+				last = h;
+			}
+		}
+		return last;
+	}
+	
+	@UIEffect public Image getImage() {
+		String imagePath = "icons/health_" + (health == 100 ? 80 : health) + ".png";
+
+		return Activator.getImage(imagePath);
+	}
+	
+	public int hashCode() {
+		return 13 * health;
+	}
+	
+	public boolean equals(Object obj) {
+		BuildHealth other = (BuildHealth) obj;
+		return health == other.health;
+	}
+
+	public int compareTo(BuildHealth o) {
+		return health - o.health;
+	}
+	
+	public String toString() {
+		return "Health: " + health;
+	}
+	
+	public int getHealth() {
+		return health;
+	}
+}

File src/dk/contix/eclipse/hudson/BuildParameter.java

+package dk.contix.eclipse.hudson;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.io.StreamCorruptedException;
+import java.util.List;
+
+import org.apache.commons.codec.binary.Base64;
+
+public class BuildParameter implements Serializable {
+	private String name;
+	private String value;
+	private String description;
+
+	public BuildParameter(String name, String value) {
+		this.name = name;
+		this.value = value;
+	}
+
+	public BuildParameter(String name, String value, String description) {
+		this.name = name;
+		this.value = value;
+		this.description = description;
+	}
+
+	public BuildParameter() {
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getValue() {
+		return value;
+	}
+
+	public void setValue(String value) {
+		this.value = value;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+	public static String serialize(List<BuildParameter> params) {
+		ByteArrayOutputStream bos = new ByteArrayOutputStream();
+		try {
+			ObjectOutputStream oos = new ObjectOutputStream(bos);
+			oos.writeObject(params);
+			oos.close();
+			
+			byte[] b = Base64.encodeBase64(bos.toByteArray());
+			
+			return new String(b);
+		} catch (IOException e) {
+			e.printStackTrace();
+			throw new RuntimeException(e);
+		}
+	}
+
+	public static List<BuildParameter> deserialize(String data) {
+		if (data == null || "".equals(data)) return null;
+
+		byte[] s = Base64.decodeBase64(data.getBytes());
+		
+		ByteArrayInputStream bis = new ByteArrayInputStream(s);
+		try {
+			ObjectInputStream ois = new ObjectInputStream(bis);
+			return (List<BuildParameter>) ois.readObject();
+		} catch (StreamCorruptedException e) {
+			return null;
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw new RuntimeException(e);
+		}
+	}
+}

File src/dk/contix/eclipse/hudson/BuildStatus.java

+package dk.contix.eclipse.hudson;
+
+import org.eclipse.swt.graphics.Image;
+
+import guitypes.checkers.quals.*;
+
+public class BuildStatus {
+	
+	private final boolean building;
+	private final Status status;
+	
+	private BuildStatus(Status status, boolean building) {
+		this.status = status;
+		this.building = building;
+	}
+
+	public static BuildStatus getStatus(String code) {
+		Status status = Status.getStatus(code);
+		if (status == null) {
+			throw new IllegalArgumentException("No status constant for value " + code);
+		}
+		boolean building = code.endsWith("_anime");
+		
+		return new BuildStatus(status, building);
+	}
+	
+	@UIEffect public Image getImage() {
+		String name = status.color;
+		if (building) {
+			name += "_anime";
+		}
+		name += ".png";
+		return Activator.getImage("icons/" + name);
+	}
+	
+	public Status getStatus() {
+		return status;
+	}
+	
+	public enum Status {
+		SUCCESS("blue"),
+		FAIL("red"),
+		TEST_FAIL("yellow"),
+		NO_BUILD("grey"),
+		DISABLED("disabled"),
+		UNKNOWN("unknown");
+		
+		private final String color;
+
+		private Status(String code) {
+			color = code;
+		}
+
+		public static Status getStatus(String code) {
+			if (code == null) {
+				return null;
+			}
+			code = code.toLowerCase();
+			if (code.endsWith("_anime")) {
+				code = code.substring(0, code.indexOf("_anime"));
+			}
+			for (Status b : values()) {
+				if (b.color.equals(code)) {
+					return b;
+				}
+			}
+			return UNKNOWN;
+		}
+}
+}

File src/dk/contix/eclipse/hudson/EasySSLProtocolSocketFactory.java

+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/contrib/org/apache/commons/httpclient/contrib/ssl/EasySSLProtocolSocketFactory.java,v 1.7 2004/06/11 19:26:27 olegk Exp $
+ * $Revision$
+ * $Date$
+ * 
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package dk.contix.eclipse.hudson;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.UnknownHostException;
+
+import org.apache.commons.httpclient.ConnectTimeoutException;
+import org.apache.commons.httpclient.HttpClientError;
+import org.apache.commons.httpclient.params.HttpConnectionParams;
+import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+
+/**
+ * <p>
+ * EasySSLProtocolSocketFactory can be used to creats SSL {@link Socket}s that accept self-signed certificates.
+ * </p>
+ * <p>
+ * This socket factory SHOULD NOT be used for productive systems due to security reasons, unless it is a concious decision and you are perfectly aware of security implications of
+ * accepting self-signed certificates
+ * </p>
+ * 
+ * <p>
+ * Example of using custom protocol socket factory for a specific host:
+ * 
+ * <pre>
+ * Protocol easyhttps = new Protocol(&quot;https&quot;, new EasySSLProtocolSocketFactory(), 443);
+ * HttpClient client = new HttpClient();
+ * client.getHostConfiguration().setHost(&quot;localhost&quot;, 443, easyhttps);
+ * // use relative url only
+ * GetMethod httpget = new GetMethod(&quot;/&quot;);
+ * client.executeMethod(httpget);
+ * </pre>
+ * 
+ * </p>
+ * <p>
+ * Example of using custom protocol socket factory per default instead of the standard one:
+ * 
+ * <pre>
+ * Protocol easyhttps = new Protocol(&quot;https&quot;, new EasySSLProtocolSocketFactory(), 443);
+ * Protocol.registerProtocol(&quot;https&quot;, easyhttps);
+ * HttpClient client = new HttpClient();
+ * GetMethod httpget = new GetMethod(&quot;https://localhost/&quot;);
+ * client.executeMethod(httpget);
+ * </pre>
+ * 
+ * </p>
+ * 
+ * @author <a href="mailto:oleg -at- ural.ru">Oleg Kalnichevski</a>
+ * 
+ * <p>
+ * DISCLAIMER: HttpClient developers DO NOT actively support this component. The component is provided as a reference material, which may be inappropriate for use without
+ * additional customization.
+ * </p>
+ */
+
+public class EasySSLProtocolSocketFactory implements SecureProtocolSocketFactory {
+
+	/** Log object for this class. */
+	private static final Log LOG = LogFactory.getLog(EasySSLProtocolSocketFactory.class);
+
+	private SSLContext sslcontext = null;
+
+	/**
+	 * Constructor for EasySSLProtocolSocketFactory.
+	 */
+	public EasySSLProtocolSocketFactory() {
+		super();
+	}
+
+	private static SSLContext createEasySSLContext() {
+		try {
+			SSLContext context = SSLContext.getInstance("SSL");
+			context.init(null, new TrustManager[] { new EasyX509TrustManager(null) }, null);
+			return context;
+		} catch (Exception e) {
+			LOG.error(e.getMessage(), e);
+			throw new HttpClientError(e.toString());
+		}
+	}
+
+	private SSLContext getSSLContext() {
+		if (this.sslcontext == null) {
+			this.sslcontext = createEasySSLContext();
+		}
+		return this.sslcontext;
+	}
+
+	/**
+	 * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int)
+	 */
+	public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException {
+
+		return getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort);
+	}
+
+	/**
+	 * Attempts to get a new socket connection to the given host within the given time limit.
+	 * <p>
+	 * To circumvent the limitations of older JREs that do not support connect timeout a controller thread is executed. The controller thread attempts to create a new socket within
+	 * the given limit of time. If socket constructor does not return until the timeout expires, the controller terminates and throws an {@link ConnectTimeoutException}
+	 * </p>
+	 * 
+	 * @param host
+	 *            the host name/IP
+	 * @param port
+	 *            the port on the host
+	 * @param clientHost
+	 *            the local host name/IP to bind the socket to
+	 * @param clientPort
+	 *            the port on the local machine
+	 * @param params
+	 *            {@link HttpConnectionParams Http connection parameters}
+	 * 
+	 * @return Socket a new socket
+	 * 
+	 * @throws IOException
+	 *             if an I/O error occurs while creating the socket
+	 * @throws UnknownHostException
+	 *             if the IP address of the host cannot be determined
+	 */
+	public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort, final HttpConnectionParams params) throws IOException,
+			UnknownHostException, ConnectTimeoutException {
+		if (params == null) {
+			throw new IllegalArgumentException("Parameters may not be null");
+		}
+		int timeout = params.getConnectionTimeout();
+		SocketFactory socketfactory = getSSLContext().getSocketFactory();
+		if (timeout == 0) {
+			return socketfactory.createSocket(host, port, localAddress, localPort);
+		} else {
+			Socket socket = socketfactory.createSocket();
+			SocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
+			SocketAddress remoteaddr = new InetSocketAddress(host, port);
+			socket.bind(localaddr);
+			socket.connect(remoteaddr, timeout);
+			return socket;
+		}
+	}
+
+	/**
+	 * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int)
+	 */
+	public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
+		return getSSLContext().getSocketFactory().createSocket(host, port);
+	}
+
+	/**
+	 * @see SecureProtocolSocketFactory#createSocket(java.net.Socket,java.lang.String,int,boolean)
+	 */
+	public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
+		return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
+	}
+
+	public boolean equals(Object obj) {
+		return ((obj != null) && obj.getClass().equals(EasySSLProtocolSocketFactory.class));
+	}
+
+	public int hashCode() {
+		return EasySSLProtocolSocketFactory.class.hashCode();
+	}
+
+}

File src/dk/contix/eclipse/hudson/EasyX509TrustManager.java

+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package dk.contix.eclipse.hudson;
+
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * <p>
+ * EasyX509TrustManager unlike default {@link X509TrustManager} accepts self-signed certificates.
+ * </p>
+ * <p>
+ * This trust manager SHOULD NOT be used for productive systems due to security reasons, unless it is a concious decision and you are perfectly aware of security implications of
+ * accepting self-signed certificates
+ * </p>
+ * 
+ * @author <a href="mailto:adrian.sutton@ephox.com">Adrian Sutton</a>
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ * 
+ * <p>
+ * DISCLAIMER: HttpClient developers DO NOT actively support this component. The component is provided as a reference material, which may be inappropriate for use without
+ * additional customization.
+ * </p>
+ */
+
+public class EasyX509TrustManager implements X509TrustManager {
+	private X509TrustManager standardTrustManager = null;
+
+	/** Log object for this class. */
+	private static final Log LOG = LogFactory.getLog(EasyX509TrustManager.class);
+
+	/**
+	 * Constructor for EasyX509TrustManager.
+	 */
+	public EasyX509TrustManager(KeyStore keystore) throws NoSuchAlgorithmException, KeyStoreException {
+		super();
+		TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+		factory.init(keystore);
+		TrustManager[] trustmanagers = factory.getTrustManagers();
+		if (trustmanagers.length == 0) {
+			throw new NoSuchAlgorithmException("no trust manager found");
+		}
+		this.standardTrustManager = (X509TrustManager) trustmanagers[0];
+	}
+
+	/**
+	 * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],String authType)
+	 */
+	public void checkClientTrusted(X509Certificate[] certificates, String authType) throws CertificateException {
+		standardTrustManager.checkClientTrusted(certificates, authType);
+	}
+
+	/**
+	 * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],String authType)
+	 */
+	public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException {
+		if ((certificates != null) && LOG.isDebugEnabled()) {
+			LOG.debug("Server certificate chain:");
+			for (int i = 0; i < certificates.length; i++) {
+				LOG.debug("X509Certificate[" + i + "]=" + certificates[i]);
+			}
+		}
+		if ((certificates != null) && (certificates.length == 1)) {
+			certificates[0].checkValidity();
+		} else {
+			standardTrustManager.checkServerTrusted(certificates, authType);
+		}
+	}
+
+	/**
+	 * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()
+	 */
+	public X509Certificate[] getAcceptedIssuers() {
+		return this.standardTrustManager.getAcceptedIssuers();
+	}
+}

File src/dk/contix/eclipse/hudson/HudsonClient.java

+package dk.contix.eclipse.hudson;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.NameValuePair;
+import org.apache.commons.httpclient.UsernamePasswordCredentials;
+import org.apache.commons.httpclient.auth.AuthScope;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.protocol.Protocol;
+import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
+import org.apache.log4j.Logger;
+import org.eclipse.core.net.proxy.IProxyData;
+import org.eclipse.core.runtime.Preferences;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+/**
+ * Class for accessing the Hudson server.
+ * 
+ * @author Joakim Recht
+ * 
+ */
+public class HudsonClient {
+	private static final Logger log = Logger.getLogger(HudsonClient.class);
+	
+	private static final Job[] EMPTY = new Job[0];
+	
+	private Preferences prefs;
+
+	public HudsonClient() {
+		prefs = Activator.getDefault().getPluginPreferences();
+
+	}
+
+	private String getBase() {
+		String b = prefs.getString(Activator.PREF_BASE_URL);
+		log.debug("Base url: " + b);
+		if (b == null || "".equals(b.trim())) {
+			return null;
+		} else {
+			return b;
+		}
+	}
+
+	public Job[] getJobs() throws IOException {
+		return getJobs(getBase());
+	}
+	
+	public Job[] getJobs(String viewUrl) throws IOException {
+		HttpClient client = getClient(viewUrl);
+		if (client == null) return EMPTY;
+		GetMethod method = new GetMethod(getRelativePath(viewUrl) + "api/xml");
+
+		try {
+			client.executeMethod(method);
+			InputStream is = method.getResponseBodyAsStream();
+			Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
+			is.close();
+
+			Element root = doc.getDocumentElement();
+			NodeList jobNodes = root.getElementsByTagName("job");
+
+			Job[] res = new Job[jobNodes.getLength()];
+			for (int i = 0; i < res.length; i++) {
+				Element jobNode = (Element) jobNodes.item(i);
+
+				String name = getNodeValue(jobNode, "name");
+				String last = getNodeValue(jobNode, "lastBuild");
+				if (last == null) {
+					// we're probably in a newer version of hudson, so we get the build number separately
+					res[i] = getJob(name, client);
+				} else {
+					Build build = new Build(last, null, null, null, 0, null);
+					String url = getNodeValue(jobNode, "url");
+
+					res[i] = new Job(name, url, build, BuildStatus.getStatus(getNodeValue(jobNode, "color")), null, null);
+				}
+			}
+
+			return res;
+		} catch (SAXException e) {
+			throw new RuntimeException(e);
+		} catch (ParserConfigurationException e) {
+			throw new RuntimeException(e);
+		} finally {
+			method.releaseConnection();
+		}
+	}
+	
+	public JobView[] getViews() throws IOException{
+		HttpClient client = getClient(getBase());
+		if (client == null) return new JobView[0];
+
+		GetMethod method = new GetMethod(getRelativePath(getBase()) + "api/xml");
+		try {
+			client.executeMethod(method);
+			InputStream is = method.getResponseBodyAsStream();
+			Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
+			is.close();
+
+			Element root = doc.getDocumentElement();
+			NodeList viewNodes = root.getElementsByTagName("view");
+
+			JobView[] res = new JobView[viewNodes.getLength()];
+			for (int i = 0; i < res.length; i++) {
+				Element jobNode = (Element) viewNodes.item(i);
+
+				String name = getNodeValue(jobNode, "name");
+				String url = getNodeValue(jobNode, "url").replaceAll(" ", "%20");
+				res[i] = new JobView(name, url);
+			}
+
+			return res;
+		} catch (SAXException e) {
+			throw new RuntimeException(e);
+		} catch (ParserConfigurationException e) {
+			throw new RuntimeException(e);
+		} finally {
+			method.releaseConnection();
+		}
+	}
+
+	private Job getJob(String name, HttpClient client) throws IOException, SAXException, ParserConfigurationException {
+		log.debug("Getting job info for " + name);
+		GetMethod method = new GetMethod(getRelativePath(getBase()) + "job/" + encode(name) + "/api/xml?depth=0");
+		try {
+			client.executeMethod(method);
+			InputStream bodyStream = method.getResponseBodyAsStream();
+			Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(bodyStream);
+			bodyStream.close();
+			
+			Element jobNode = doc.getDocumentElement();
+
+			String jobUrl = getNodeValue(jobNode, "url");
+			String jobStatus = getNodeValue(jobNode, "color");
+
+			String lastBuildNumber = getNodeValue(getChild("lastBuild", jobNode), "number");
+			String lastBuildUrl = getNodeValue(getChild("lastBuild", jobNode), "url");
+			
+			List<String> healthScore = getHealthScore(jobNode);
+			
+			BuildHealth health = BuildHealth.getLowest(healthScore);
+
+
+			List<BuildParameter> defaultParameters = getDefaultParameters(name, client);
+
+			Build lastBuild = null;
+			if (lastBuildNumber != null) {
+				lastBuild = getLastBuild(name, lastBuildNumber, lastBuildUrl, client);
+			} else {
+				log.debug("ERROR: last build not available for Job '" + name + "'");
+			}
+			return new Job(name, jobUrl, lastBuild, BuildStatus.getStatus(jobStatus), health, defaultParameters);
+		} finally {
+			method.releaseConnection();
+		}
+	}
+
+	private List<BuildParameter> getDefaultParameters(String name, HttpClient client) throws IOException, SAXException, ParserConfigurationException {
+		log.debug("Getting deafult parameter info for " + name);
+		GetMethod method = new GetMethod(getRelativePath(getBase()) + "job/" + encode(name) + "/api/xml");
+		try {
+			client.executeMethod(method);
+			InputStream bodyStream = method.getResponseBodyAsStream();
+			Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(bodyStream);
+			bodyStream.close();
+
+			List<BuildParameter> parameters = new ArrayList<BuildParameter>();
+
+			Element root = doc.getDocumentElement();
+			NodeList actionNodes = root.getElementsByTagName("action");
+
+			for (int i = 0; i < actionNodes.getLength(); i++) {
+				Element actionNode = (Element) actionNodes.item(i);
+				NodeList parameterNodes = actionNode.getElementsByTagName("parameterDefinition");
+				for (int j = 0; j < parameterNodes.getLength(); j++) {
+					Element parameterNode = (Element) parameterNodes.item(j);
+					Element valueNode = (Element) parameterNode.getElementsByTagName("defaultParameterValue").item(0);
+					parameters.add(new BuildParameter(getNodeValue(parameterNode, "name"),getNodeValue(valueNode, "value"),getNodeValue(parameterNode, "description")));
+				}
+			}
+			return parameters;
+		} finally {
+			method.releaseConnection();
+		}
+	}
+	
+	private Build getLastBuild(String name, String number, String url, HttpClient client) throws IOException, SAXException, ParserConfigurationException {
+		log.debug("Getting build info for Job '" + name + "' and Build '" + number + "'");
+		GetMethod method = new GetMethod(getRelativePath(getBase()) + "job/" + encode(name) + "/" + number + "/api/xml");
+		try {
+			client.executeMethod(method);
+			InputStream bodyStream = method.getResponseBodyAsStream();
+			Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(bodyStream);
+			bodyStream.close();
+
+			Element root = doc.getDocumentElement();
+
+			String status = getNodeValue(root, "status");
+			String id = getNodeValue(root, "id");
+			String timestamp = getNodeValue(root, "timestamp");
+
+			List<BuildParameter> parameters = getLastBuildParameters(name, number, root, client);
+
+			return new Build(number, url, status, id, Long.valueOf(timestamp).longValue(), parameters);
+		} finally {
+			method.releaseConnection();
+		}
+	}
+
+	private List<BuildParameter> getLastBuildParameters(String name, String number, Element root, HttpClient client) throws IOException, SAXException, ParserConfigurationException {
+		log.debug("Getting parameter info for Job '" + name + "' and Build '" + number + "'");
+		List<BuildParameter> parameters = new ArrayList<BuildParameter>();
+
+		NodeList actionNodes = root.getElementsByTagName("action");
+
+		for (int i = 0; i < actionNodes.getLength(); i++) {
+			Element actionNode = (Element) actionNodes.item(i);
+			NodeList parameterNodes = actionNode.getElementsByTagName("parameter");
+			for (int j = 0; j < parameterNodes.getLength(); j++) {
+				Element parameterNode = (Element) parameterNodes.item(j);
+				parameters.add(new BuildParameter(getNodeValue(parameterNode, "name"),getNodeValue(parameterNode, "value")));
+			}
+		}
+		return parameters;
+	}
+
+	private List<String> getHealthScore(Element jobNode) {
+		List<String> healthScore = new ArrayList<String>();
+		NodeList nodes = jobNode.getChildNodes();
+		for (int i = 0; i < nodes.getLength(); i++) {
+			Node n = nodes.item(i);
+			if ("healthReport".equals(n.getNodeName())) {
+				healthScore.add(getNodeValue((Element) n, "score"));
+			}
+		}
+
+		return healthScore;
+	}
+
+	private Element getChild(String name, Element parent) {
+		NodeList nodes = parent.getChildNodes();
+		for (int i = 0; i < nodes.getLength(); i++) {
+			Node n = nodes.item(i);
+			if (name.equals(n.getNodeName())) {
+				return (Element) n;
+			}
+		}
+		return null;
+	}
+	
+	private String encode(String url) {
+		try {
+			return URLEncoder.encode(url, "UTF-8").replaceAll("\\+", "%20");
+		} catch (UnsupportedEncodingException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public void scheduleJob(String project, List<BuildParameter> parameters) throws IOException, ParametersRequiredException {
+		HttpClient client = getClient(getBase());
+		String st = prefs.getString(Activator.PREF_SECURITY_TOKEN + "_" + project);
+
+		List<NameValuePair> query = new ArrayList<NameValuePair>();
+		if (st != null && st.length() > 0) {
+			query.add(new NameValuePair("token", st));
+		}
+		
+
+		String path = "/build";
+		if (parameters != null && parameters.size() > 0) {
+			path += "WithParameters";
+			
+			for (BuildParameter p : parameters) {
+				if (p.getName() != null && !"".equals(p.getName().trim())) {
+					query.add(new NameValuePair(p.getName(), p.getValue()));
+				}
+			}
+		}
+
+		GetMethod method = new GetMethod(getRelativePath(getBase()) + "job/" + encode(project) + path);
+		if (query.size() > 0) {
+			method.setQueryString(query.toArray(new NameValuePair[query.size()]));
+		}
+
+		try {
+			int res = client.executeMethod(method);
+			log.debug("Build schedule result: " + res);
+			if (res == HttpStatus.SC_FORBIDDEN) {
+				throw new IOException("Scheduling failed, security token required");
+			} else if (res == HttpStatus.SC_METHOD_NOT_ALLOWED) {
+				throw new ParametersRequiredException();
+			}
+			method.getResponseBodyAsStream().close();
+		} finally {
+			method.releaseConnection();
+		}
+	}
+
+	public void scheduleJob(String project) throws IOException, ParametersRequiredException {
+		scheduleJob(project, null);
+	}
+
+	public void checkValidUrl(String base, boolean authEnabled, String username, String password) throws Exception {
+
+		HttpClient client = getClient(base, authEnabled, username, password);
+		GetMethod method = new GetMethod(getRelativePath(base) + "api/xml");
+
+		try {
+			int res = client.executeMethod(client.getHostConfiguration(), method);
+			if (res == HttpStatus.SC_NOT_FOUND) {
+				throw new IllegalArgumentException("No content found at " + method.getPath());
+			} else if (res == HttpStatus.SC_UNAUTHORIZED) {
+				throw new IllegalArgumentException("Basic authentication required for " + method.getPath());
+			}
+			InputStream bodyStream = method.getResponseBodyAsStream();
+			Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(bodyStream);
+			bodyStream.close();
+
+			Element root = doc.getDocumentElement();
+			if (root.getChildNodes().getLength() == 0 || !root.getNodeName().equals("hudson")) {
+				throw new IllegalArgumentException("URL does not point to a valid Hudson installation. /api/xml does not return correct data.");
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw e;
+		} finally {
+			method.releaseConnection();
+		}
+	}
+
+	private String getNodeValue(Element node, String name) {
+		if (node == null) return null;
+		NodeList nodes = node.getChildNodes();
+		for (int i = 0; i < nodes.getLength(); i++) {
+			Node n = nodes.item(i);
+			if (name.equals(n.getNodeName())) {
+				return n.getTextContent().trim();
+			}
+		}
+		return null;
+	}
+
+	private HttpClient getClient(String base) throws IOException {
+		return getClient(base, prefs.getBoolean(Activator.PREF_USE_AUTH), prefs.getString(Activator.PREF_LOGIN), prefs.getString(Activator.PREF_PASSWORD));
+	}
+
+	private HttpClient getClient(String base, boolean authEnabled, String username, String password) throws IOException {
+		if (base == null) return null;
+		
+		try {
+			HttpClient client = new HttpClient();
+			String type;
+			URL u = new URL(base);
+			int port = u.getPort();
+			if (u.getProtocol().equalsIgnoreCase("https")) {
+				if (port == -1) {
+					port = 443;
+				}
+				type = IProxyData.HTTPS_PROXY_TYPE;
+				client.getHostConfiguration().setHost(u.getHost(), port, new Protocol("https", (ProtocolSocketFactory) new EasySSLProtocolSocketFactory(), 443));
+			} else {
+				if (port == -1) {
+					port = 80;
+				}
+				type = IProxyData.HTTP_PROXY_TYPE;
+				client.getHostConfiguration().setHost(u.getHost(), port);
+			}
+			IProxyData proxyData = Activator.getDefault().getProxyService().getProxyDataForHost(u.getHost(), type);
+			if (proxyData != null) {
+				client.getHostConfiguration().setProxy(proxyData.getHost(), proxyData.getPort());
+				if (proxyData.isRequiresAuthentication()) {
+					client.getState().setProxyCredentials(new AuthScope(proxyData.getHost(), proxyData.getPort()),
+							new UsernamePasswordCredentials(proxyData.getUserId(), proxyData.getPassword()));
+				}
+			}
+			client.getParams().setConnectionManagerTimeout(1000);
+			client.getHttpConnectionManager().getParams().setConnectionTimeout(2000);
+			client.getParams().setSoTimeout(3000);
+			
+			//submits a GET to the security servlet with user and password as parameters
+			if (authEnabled) {
+				log.debug("Auth is enabled, username: " + username);
+				GetMethod getMethod = new GetMethod(getRelativePath(base) + "j_acegi_security_check");
+				getMethod.setQueryString("j_username=" + username + "&j_password="+password);
+				int res = client.executeMethod(getMethod);
+				getMethod.releaseConnection();
+				if (res == HttpStatus.SC_NOT_FOUND) {
+					getMethod = new GetMethod(getRelativePath(base) + "j_security_check");
+					getMethod.setQueryString("j_username=" + username + "&j_password="+password);
+					res = client.executeMethod(getMethod);
+				} else if (res == HttpStatus.SC_UNAUTHORIZED) {
+					client.getParams().setAuthenticationPreemptive(true);
+					client.getState().setCredentials(new AuthScope(u.getHost(), port), new UsernamePasswordCredentials(username, password));
+				}
+				log.debug("Login result for " + getMethod.getURI() + ": " + res);
+			}
+
+			return client;
+		} catch (MalformedURLException e1) {
+			throw new RuntimeException(e1);
+		}
+	}
+
+	private String getRelativePath(String url) {
+		int pos = url.indexOf('/', 8);
+		if (pos == -1) {
+			return "/";
+		} else {
+			String path = url.substring(pos);
+			if (!path.endsWith("/")) {
+				path += "/";
+			}
+			return path;
+		}
+	}
+
+}

File src/dk/contix/eclipse/hudson/Job.java

+package dk.contix.eclipse.hudson;
+
+import java.util.List;
+
+/**
+ * Representation of a Hudson job.
+ * 
+ * @author Joakim Recht
+ *
+ */
+public class Job {
+	
+	private String name;
+	private String url;
+	private Build lastBuild;
+	private BuildStatus status;
+	private final BuildHealth health;
+	private List<BuildParameter> defaultParameters;
+
+	public Job(String name, String url, Build lastBuild, BuildStatus status, BuildHealth health, List<BuildParameter> defaultParameters) {
+		super();
+		this.name = name;
+		this.url = url;
+		this.lastBuild = lastBuild;
+		this.status = status;
+		this.health = health;
+		this.defaultParameters = defaultParameters;
+	}
+	public BuildStatus getStatus() {
+		return status;
+	}
+	public String getName() {
+		return name;
+	}
+	public String getUrl() {
+		return url;
+	}
+	public Build getLastBuild() {
+		return lastBuild;
+	}
+	public BuildHealth getHealth() {
+		return health;
+	}
+
+	public List<BuildParameter> getDefaultParameters() {
+		return defaultParameters;
+	}
+	public void setDefaultParameters(List<BuildParameter> defaultParameters) {
+		this.defaultParameters = defaultParameters;
+	}
+
+	public int hashCode() {
+		final int PRIME = 31;
+		int result = 1;
+		result = PRIME * result + ((status == null) ? 0 : status.hashCode());
+		result = PRIME * result + ((lastBuild == null) ? 0 : lastBuild.hashCode());
+		result = PRIME * result + ((name == null) ? 0 : name.hashCode());
+		result = PRIME * result + ((url == null) ? 0 : url.hashCode());
+		result = PRIME * result + ((health == null) ? 0 : health.hashCode());
+		return result;
+	}
+
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		final Job other = (Job) obj;
+		if (name == null) {
+			if (other.name != null)
+				return false;
+		} else if (!name.equals(other.name))
+			return false;
+		if (url == null) {
+			if (other.url != null)
+				return false;
+		} else if (!url.equals(other.url))
+			return false;
+		if (lastBuild == null) {
+			if (other.lastBuild != null)
+				return false;
+		} else if (!lastBuild.equals(other.lastBuild))
+			return false;
+		if (health == null) {
+			if (other.health != null)
+				return false;
+		} else if (!health.equals(other.health))
+			return false;
+		return true;
+	}
+}

File src/dk/contix/eclipse/hudson/JobContentProvider.java

+package dk.contix.eclipse.hudson;
+
+import org.apache.log4j.Logger;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Preferences;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.Preferences.PropertyChangeEvent;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.widgets.Display;
+
+import dk.contix.eclipse.hudson.views.actions.BuildStatusAction;
+
+import guitypes.checkers.quals.*;
+
+/**
+ * Content Provider for job listing.
+ * 
+ * @author Joakim Recht
+ * 
+ */
+public class JobContentProvider implements IStructuredContentProvider {
+	private static final Logger log = Logger.getLogger(JobContentProvider.class);
+
+	private org.eclipse.core.runtime.jobs.Job updateJob;
+
+	private final HudsonClient client = new HudsonClient();
+
+	private Preferences prefs;
+
+	private final TableViewer viewer;
+
+	private Job[] jobs = new Job[0];
+
+	private boolean updating = false;
+
+	private boolean error = false;
+
+	private final BuildStatusAction action;
+
+	private String viewUrl;
+
+	public JobContentProvider(final TableViewer viewer, BuildStatusAction action) {
+		this.viewer = viewer;
+		this.action = action;
+		prefs = Activator.getDefault().getPluginPreferences();
+		prefs.addPropertyChangeListener(new Preferences.IPropertyChangeListener() {
+			public void propertyChange(PropertyChangeEvent event) {
+				error = false;
+				reloadUpdateJob();
+			}
+		});
+		reloadUpdateJob();
+	}
+
+	private void reloadUpdateJob() {
+		if (updateJob != null) {
+			updateJob.cancel();
+		}
+
+		if (prefs.getBoolean(Activator.PREF_AUTO_UPDATE)) {
+			updateJob = new org.eclipse.core.runtime.jobs.Job("Fetch Hudson status") {
+				protected IStatus run(IProgressMonitor monitor) {
+					monitor.beginTask("Refreshing Hudson status", 1);
+					refresh();
+					try {
+						Display.getDefault().asyncExec(new @UI Runnable() {
+							public void run() {
+								viewer.refresh();
+								updateJob.schedule(prefs.getInt(Activator.PREF_UPDATE_INTERVAL) * 1000);
+							}
+						});
+						return Status.OK_STATUS;
+					} finally {
+						monitor.done();
+					}
+				}
+			};
+			updateJob.setPriority(org.eclipse.core.runtime.jobs.Job.DECORATE);
+			updateJob.schedule(1500);
+		}
+
+	}
+	
+	public void refresh() {
+		if (updating) return;
+		
+		try {
+			updating = true;
+			final Job[] newJobs;
+			if (viewUrl == null) {
+				newJobs = client.getJobs();
+			} else {
+				newJobs = client.getJobs(viewUrl);
+			}
+
+			if (jobs != null && prefs.getBoolean(Activator.PREF_POPUP_ON_ERROR)) {
+				for (int i = 0; i < newJobs.length; i++) {
+					if (hasFailed(newJobs[i])) {
+						final Job job = newJobs[i];
+						// Bug note by Sai Zhang, the following code should be wrapped in
+						// Display.getDefault().syncExec(new Runnable() {  public void run() {} { .... }} // Colin Gordon: BUG
+						/* BUG -- Colin Gordon */ MessageDialog.openWarning(viewer.getControl().getShell(), "Hudson build failed", "Hudson build failed for " + job.getName());
+						break;
+					}
+				}
+			}
+			check: {
+				for (int i = 0; i < newJobs.length; i++) {
+					if (newJobs[i].getStatus().getStatus() == dk.contix.eclipse.hudson.BuildStatus.Status.FAIL || newJobs[i].getStatus().getStatus() == dk.contix.eclipse.hudson.BuildStatus.Status.TEST_FAIL) {
+						if (!ignore(newJobs[i])) {
+							final Job j = newJobs[i];
+							Display.getDefault().syncExec(new @UI Runnable() {
+								public void run() {
+									action.setError(j);
+								}
+							});
+							break check;
+						}
+					}
+				}
+				Display.getDefault().syncExec(new @UI Runnable() {
+					public void run() {
+						action.setOk();
+					}
+				});
+			}
+
+			jobs = newJobs;
+			error = false;
+		} catch (final RuntimeException e) {
+			log.error("Unable to get jobs", e);
+			// Bug note by Sai Zhang, the following code should be wrapped in
+			// Display.getDefault().syncExec(new Runnable() {  public void run() {} { .... }}
+			/* BUG -- Colin Gordon */ action.setUnknown();
+			/* BUG -- Colin Gordon */ ErrorDialog.openError(viewer.getControl().getShell(), "Unable to get Hudson status",
+					"Unable to get status from Hudson.", new Status(Status.ERROR, Activator.PLUGIN_ID, 0,
+							"Unable to communicate with Hudson", e));
+			
+			error = true;
+			jobs = new Job[0];
+		} catch (final Exception e) {
+			log.error("Unable to get jobs", e);
+			// Bug note by Sai Zhang, the following code should be wrapped in
+			// Display.getDefault().syncExec(new Runnable() {  public void run() {} { .... }}
+			/* BUG -- Colin Gordon */ action.setUnknown();
+			if (!error && prefs.getBoolean(Activator.PREF_POPUP_ON_CONNECTION_ERROR)) {
+				/* BUG -- Colin Gordon */ ErrorDialog.openError(viewer.getControl().getShell(), "Unable to get Hudson status",
+						"Unable to get status from Hudson. Check that the base url is configured correctly under preferences.", new Status(Status.ERROR, Activator.PLUGIN_ID, 0,
+								"Unable to communicate with Hudson", e));
+			}
+			error = true;
+			jobs = new Job[0];
+		} finally {
+			updating = false;
+		}
+
+	}
+
+	public Object[] getElements(Object inputElement) {
+		return jobs;
+	}
+
+	private boolean hasFailed(Job job) {
+		if (ignore(job)) {
+			return false;
+		}
+		for (int i = 0; i < jobs.length; i++) {
+			if (jobs[i].getName().equals(job.getName())) {
+				return job.getStatus().getStatus() == dk.contix.eclipse.hudson.BuildStatus.Status.FAIL && jobs[i].getStatus().getStatus() == dk.contix.eclipse.hudson.BuildStatus.Status.SUCCESS;
+			}
+		}
+		return false;
+	}
+
+	private boolean ignore(Job job) {
+		return prefs.getBoolean(Activator.PREF_FILTER_IGNORE_PROJECT + "_" + job.getName());
+	}
+
+	public void dispose() {
+		if (updateJob != null) {
+			updateJob.cancel();
+		}
+	}
+
+	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+	}
+
+	public void setView(String url) {
+		this.viewUrl = url;
+	}
+
+}

File src/dk/contix/eclipse/hudson/JobView.java

+package dk.contix.eclipse.hudson;
+
+public class JobView {
+	private final String name;
+	private final String url;
+	
+	public JobView(String name, String url) {
+		this.name = name;
+		this.url = url;
+	}
+	
+	public String getName() {
+		return name;
+	}
+	
+	public String getUrl() {
+		return url;
+	}
+}

File src/dk/contix/eclipse/hudson/ParametersRequiredException.java

+package dk.contix.eclipse.hudson;
+
+public class ParametersRequiredException extends Exception {
+
+}

File src/dk/contix/eclipse/hudson/digger/DiggerDialog.java

+package dk.contix.eclipse.hudson.digger;
+
+import java.util.Collection;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.ProgressBar;
+import org.eclipse.swt.widgets.Shell;