package org.hudson.trayapp.model;
import java.io.IOException;
import java.io.Writer;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Iterator;
import java.util.Vector;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class Model{
public final static String[] colours = {"red_anime","red","yellow_anime","yellow","blue_anime","blue","disabled_anime","disabled","aborted_anime","aborted"};
private List servers;
private List previousServers;
public Model() {
servers = new Vector();
previousServers = new Vector();
}
public Object clone() {
Model modelReturn = new Model();
Iterator iterator = servers.iterator();
while (iterator.hasNext()) {
Server server = (Server) iterator.next();
modelReturn.servers.add(server.clone());
}
return modelReturn;
}
public int getServerModelSize() {
return servers.size();
}
public Server getServerAt(int index) {
return (Server) servers.get(index);
}
public int getServerIndex(String url) {
return getServerIndex(url, false);
}
public int getServerIndex(String url, boolean previous) {
Iterator iterServers;
if (previous)
iterServers = previousServers.iterator();
else
iterServers = servers.iterator();
int i = 0;
while (iterServers.hasNext()) {
if (url.equals(((Server) iterServers.next()).getURL()))
return i;
i++;
}
return -1;
}
public void addServer(String url, String name) {
servers.add(new Server(url, name));
}
public void removeServer(String url) {
int index = getServerIndex(url);
if (index != -1) {
servers.remove(index);
}
}
public boolean containsServer(String url) {
return getServerIndex(url) != -1;
}
public void update() {
/*
* Firstly we need to take a backup of the existing server state. This will
* enable us to decide what actions we need to take once we've updated.
*/
previousServers.clear();
Iterator iterServers = servers.iterator();
while (iterServers.hasNext()) {
Server server = (Server) iterServers.next();
previousServers.add(server.clone());
}
/*
* Now we'll process an update on all the servers
*/
iterServers = servers.iterator();
while (iterServers.hasNext()) {
Server server = (Server) iterServers.next();
if (server.getURL().length() > 0) {
server.update();
}
}
}
/**
* This method will pull all of the worst jobs from each server, returning the
* worst of the worst jobs overall. I.e. if you have 3 servers, and each server has 1 job
* each, and that the 3 jobs in total are blue, yellow, and red, it will return the job
* from the server that has the red job.
* @param fromPrevious This flag should be set to true, if you want to get the worst jobs
* from the previous update, or false if you want the current worst jobs.
* @return This returns a collection (never null) of jobs that are the worst jobs overall.
*/
public List getWorstJobs(boolean fromPrevious) {
Vector vecWorst = new Vector();
int worstCase = -1;
Iterator serveriter;
if (fromPrevious) {
serveriter = previousServers.iterator();
} else {
serveriter = servers.iterator();
}
while (serveriter.hasNext()) {
Server server = (Server) serveriter.next();
String colour = server.getColour();
// Note, the first time this runs, the previous colour will be null
if (colour != null) {
for (int i = 0; worstCase != i && i < Model.colours.length; i++) {
if (colour.equals(Model.colours[i])) {
worstCase = i;
vecWorst.clear();
}
}
if (colour.equals(Model.colours[worstCase])) {
Job[] jobs = server.getWorstJobs();
for (int i = 0; i < jobs.length; i++) {
vecWorst.add(jobs[i]);
}
}
}
}
return vecWorst;
}
public int getWorstJobsWorstHealth() {
List worstJobs = getWorstJobs(false);
int health = 101;
for (int i = 0; i < worstJobs.size(); i++) {
int jobHealth = ((Job) worstJobs.get(i)).getWorstHealthScore();
if (jobHealth != -1) {
health = Math.min(health, jobHealth);
}
}
if (health == 101){
health = -1;
}
return health;
}
/**
* This method will return the worst colour as a string across all of the servers.
* @param fromPrevious This flag should be set to true, if you want to get the worst colour
* from the previous update, or false if you want the current worst colour.
* @return This returns the current/previous worst colour, or "grey" if there are no jobs/servers defined
*/
public String getWorstColour(boolean fromPrevious) {
Collection jobs = getWorstJobs(fromPrevious);
if (jobs.isEmpty()) {
return "grey";
} else {
return ((Job) jobs.iterator().next()).getColour();
}
}
/**
* This method will examine the previous and current states, and determine whether or not
* the build has changed. It uses a couple of stages to determine this, see the returns
* declaration below:
* @return If any servers in the previous list aren't in the new list, this will return true.\n
* If any of the current servers aren't in the previous list, this will return true.\n
* If any of the previous worst jobs aren't in the new worst jobs list, this will return true.\n
* If any of the current previous jobs aren't in the previous worst jobs list, this will return true.\n
* If after checking the above, nothing has changed, then it will inspect each job, and check
* the build numbers. If any of the build numbers have changed, then this will return true.\n
* Otherwise, this will return false
*/
public boolean getBuildChanged() {
//If any servers in the previous list aren't in the new list, this will return true
Iterator iterServerEntries = previousServers.iterator();
while (iterServerEntries.hasNext()) {
if (getServerIndex(((Server) iterServerEntries.next()).getURL(), false) == -1) {
// We have at least one server that has been removed, return true.
return true;
}
}
//If any of the current servers aren't in the previous list, this will return true.
iterServerEntries = servers.iterator();
while (iterServerEntries.hasNext()) {
if (getServerIndex(((Server) iterServerEntries.next()).getURL(), true) == -1) {
// We have at least one server that has been added, return true.
return true;
}
}
// If any of the previous worst jobs aren't in the new worst jobs list, this will return true.
if (getJobsLeftWorstBuild().size() > 0)
return true;
// If any of the current previous jobs aren't in the previous worst jobs list, this will return true.
if (getJobsJoinedWorstBuild().size() > 0)
return true;
/*
* If after checking the above, nothing has changed, then it will inspect each job, and check
* the build numbers. If any of the build numbers have changed, then this will return true.\n
*/
Collection jobsPrevious = getWorstJobs(true);
Collection jobsCurrent = getWorstJobs(false);
Map mapPrevious = new HashMap(jobsPrevious.size());
Iterator iterJobs = jobsPrevious.iterator();
while (iterJobs.hasNext()) {
Job job = (Job) iterJobs.next();
mapPrevious.put(job.getUrl(), job);
}
iterJobs = jobsCurrent.iterator();
while (iterJobs.hasNext()) {
Job jobCurrent = (Job) iterJobs.next();
Job jobPrevious = (Job) mapPrevious.get(jobCurrent.getUrl());
if (jobCurrent.getLastBuild() == null && jobPrevious.getLastBuild() != null) {
// we have a mismatch between builds, this is a change
return true;
} else if (jobCurrent.getLastBuild() != null && jobPrevious.getLastBuild() == null) {
// we have a mismatch between builds, this is a change
return true;
} else if (jobCurrent.getLastBuild() != null && jobPrevious.getLastBuild() != null && jobCurrent.getLastBuild().getNumber() != jobPrevious.getLastBuild().getNumber()) {
/*
* We have at least one job in the worst job list that has changed it's build number
* thus we'll return true.
*/
return true;
}
}
// Otherwise, this will return false
return false;
}
public List getJobsLeftWorstBuild() {
List lstReturn = new Vector();
Collection jobsPrevious = getWorstJobs(true);
Collection jobsCurrent = getWorstJobs(false);
Iterator iterJobs = jobsPrevious.iterator();
while (iterJobs.hasNext()) {
Job job = (Job) iterJobs.next();
if (!(jobsCurrent.contains(job))) {
lstReturn.add(job);
}
}
return lstReturn;
}
public List getJobsJoinedWorstBuild() {
List lstReturn = new Vector();
Collection jobsPrevious = getWorstJobs(true);
Collection jobsCurrent = getWorstJobs(false);
// If any of the previous worst jobs aren't in the new worst jobs list, this will return true.
Iterator iterJobs = jobsCurrent.iterator();
while (iterJobs.hasNext()) {
Job job = (Job) iterJobs.next();
if (!(jobsPrevious.contains(job))) {
// We have at least one Job that has moved out of the worst job list, return true.
lstReturn.add(job);
}
}
return lstReturn;
}
public void writeXML(Writer w) throws IOException {
w.write("<model>");
Iterator iterServers = servers.iterator();
while (iterServers.hasNext()) {
((Server) iterServers.next()).writeXML(w);
}
w.write("</model>");
}
public void process(NodeList nodes) {
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
String name = node.getNodeName();
if (name.equals("model")) {
process(node.getChildNodes());
} else if (name.equals("server")) {
Server server = new Server();
server.process(node.getChildNodes());
servers.add(server);
}
}
}
/**
* This method will traverse all of the Jobs, and determine the total
* of all of the jobs that share the same colour that you pass in
* @param colour The colour you want to get a count for.
* @return The total number of jobs that are the same state as the colour provided
*/
public int getNumberOfJobsWithColour(String colour) {
int iReturn = 0;
Iterator serveriter = servers.iterator();
while (serveriter.hasNext()) {
iReturn += ((Server) serveriter.next()).getNumberOfJobsWithColour(colour);
}
return iReturn;
}
/**
* This method is provided as a convenience method to determine the total
* number of jobs that are currently red (this includes those building).
* @return The total number of jobs that are in a Red state (including those building).
*/
public int getNumberOfRedJobs() {
return getNumberOfJobsWithColour("red") + getNumberOfJobsWithColour("red_anime");
}
/**
* This method is provided as a convenience method to determine the total
* number of jobs that are currently yellow (this includes those building).
* @return The total number of jobs that are in a Yellow state (including those building).
*/
public int getNumberOfYellowJobs() {
return getNumberOfJobsWithColour("yellow") + getNumberOfJobsWithColour("yellow_anime");
}
/**
* This method is provided as a convenience method to determine the total
* number of jobs that are currently blue (this includes those building).
* @return The total number of jobs that are in a Blue state (including those building).
*/
public int getNumberOfBlueJobs() {
return getNumberOfJobsWithColour("blue") + getNumberOfJobsWithColour("blue_anime");
}
/**
* This method is provided as a convenience method to determine the total
* number of jobs that are currently grey.
* @return The total number of jobs that are in a grey state.
*/
public int getNumberOfGreyJobs() {
return getNumberOfJobsWithColour("grey");
}
/**
* This method is provided as a convenience method to determine the total
* number of jobs that are currently building.
* @return The total number of jobs that are building.
*/
public int getNumberOfBuildingJobs() {
return getNumberOfJobsWithColour("red_anime") + getNumberOfJobsWithColour("yellow_anime")
+ getNumberOfJobsWithColour("blue_anime");
}
}