My first Hudson PlugIn

I set up a Hudson instance in my company. But after adding the 20th job I lost the overview: which job depends on which other job? I need a visualization.
I need a plugin which reads the job dependencies and creates a dependency graph.

This problem consists of two parts: getting te dependencies and creating the graph. And I need to solve both for getting the plugin work …

I start with creating the graph. Shouldn’t be a problem – I am sure there are some libraries I could use as dependency graphs are an old and common ‚problem‘. And searching for „java create dependency graph“ gave me a nice hit: yFiles.
But this lib has an enourmous drawback: it is commercial and the plugin should be published as OSS.
But I remembered another tool: Graphiz. And via its homepage I found a Java layer on top of it: Linguine Maps.

First played a little bit:

String dotExeFileName = "C:/temp/ec-dep/Job Dependency Visualizer/lib/graphiz/dot.exe";
 // create the board for the graph
 Graph graph = GraphFactory.newGraph();
 // create the nodes
 GraphNode child   = graph.addNode();
 GraphNode me      = graph.addNode();
 GraphNode wife    = graph.addNode();
 GraphNode father  = graph.addNode();
 GraphNode mother  = graph.addNode();
 GraphNode grandma = graph.addNode();
 GraphNode grandpa = graph.addNode();
 GraphNode friend  = graph.addNode();
 // connect the nodes according to the dependencies
 graph.addEdge(me, child);
 graph.addEdge(wife, child);
 graph.addEdge(father, me);
 graph.addEdge(mother, me);
 graph.addEdge(grandma, father);
 graph.addEdge(grandpa, father);
 // nodes have a caption
 child.getInfo().setCaption("Child");
 me.getInfo().setCaption("Me");
 wife.getInfo().setCaption("Wife");
 father.getInfo().setCaption("Father");
 mother.getInfo().setCaption("Mother");
 grandma.getInfo().setCaption("Grandma");
 grandpa.getInfo().setCaption("Grandpa");
 friend.getInfo().setCaption("Friend");
 // highlight some nodes
 me.getInfo().setFillColor("red");
 grandma.getInfo().setModeDotted();
 grandpa.getInfo().setModeDotted();
 // save as gif
 File dotFileName = File.createTempFile("tmp.graph.", ".dot");
 GRAPHtoDOTtoGIF.transform(
 graph,
 dotFileName.getAbsolutePath(),
 "C:/temp/graph.demo-old.gif",
 dotExeFileName
 );

graph.demo-old

Oh, this is the library I want to use. So I wrote a class tayloring the graph creation (Java 1.4) to dependency graphs with use of Java >=5 and a fluent interface.


DependencyGraph
 .create()
 .addDependency("Child", "Me", "Wife")
 .addDependency("Me", "Father", "Mother")
 .addDependency("Father", "Grandma", "Grandpa")
 .addDependency("Friend")
 .nodeBorder("Grandma", BorderStyle.BORDER_DOTTED)
 .nodeBorder("Grandpa", BorderStyle.BORDER_DOTTED)
 .highlight("Me", Style.FILL_COLOR, "red")
 .toGif("C:/temp/graph.demo.gif");

My base concepts are:

  • a string could have dependency on other strings (me–>father,mother; wife:<none>)
  • the string is the caption of the graph node
  • the string is the unique node name
  • two strings can only be connected once

So I could store the dependencies as a map: Map<String,Set<String>>


public class DependencyGraph {

 /** Path to the Graphiz executable. */
 private String dotExeFileName = "C:/temp/ec-dep/Job Dependency Visualizer/lib/graphiz/dot.exe";

 /** The graph to create. */
 Graph graph = GraphFactory.newGraph();

/** All already created GraphNodes. */
 Map<String, GraphNode> nodes = new Hashtable<String, GraphNode>();

 /** Already created dependency lines. */
 Map<String, Set<String>> dependencies = new Hashtable<String, Set<String>>();

 public static DependencyGraph create() {
 return new DependencyGraph();
 }

/**
 * Adds a node and its dependencies to the graph. If the node exists
 * already, it is not created a second time.
 */
 public DependencyGraph addDependency(String node, String... parents) {
 Set<String> list = new HashSet<String>();
 Collections.addAll(list, parents);
 addDependency(node, list);
 return this;
 }

public DependencyGraph addDependency(String node, Set<String> parents) {
 createGraphNode(node);
 for (String parent : parents) {
 createGraphNode(parent);
 if (!dependencies.get(node).contains(parent)) {
 // create the link
 graph.addEdge(nodes.get(parent), nodes.get(node));
 // register the dependency
 dependencies.get(node).add(parent);
 }
 }
 return this;
 }

/**
 * Helper method for creating graph nodes and store them in the cache if the
 * don't exist before.
 */
 private void createGraphNode(String nodeName) {
 if (!nodes.containsKey(nodeName)) {
 GraphNode node = graph.addNode();
 node.getInfo().setCaption(mask(nodeName));
 nodes.put(nodeName, node);
 dependencies.put(nodeName, new HashSet<String>());
 }
 }

/**
 * Modify the shape style of a node.
 *
 * @param node the name of the node to style
 * @param style the new style
 * @return self
 */
 public DependencyGraph nodeStyle(String node, ShapeStyle style) {
 nodes.get(node).getInfo().setShape(style.value);
 return this;
 }

/**
 * Stores the graph as GIF image.
 * @param file the file to new image file
 * @return self
 * @throws IOException while file access
 */
 public DependencyGraph toGif(File file) throws IOException {
 File dotFileName = File.createTempFile("tmp.graph.", ".dot");
 GRAPHtoDOTtoGIF.transform(
 graph, dotFileName.getAbsolutePath(),
 file.getAbsolutePath(), dotExeFileName);
 dotFileName.delete();
 return this;
 }

}

public enum ShapeStyle {
 RECORD(0),
 BOX(1),
 ELLIPSE(2),
 CIRCLE(3),
 DIAMOND(4),
 TRIANGLE(5),
 INVTRIANGLE(6),
 HEXAGON(7),
 OCTAGON(8),
 PARALLELOGRAM(9);

 /** the LinguineMaps style value */
 int value;
 private ShapeStyle(int value) {
 this.value = value;
 }

}

After solving the second part of my problem I have to solve the first part too. I need information from Hudson:

  • List<Job> getAllJobs()
  • List<Job> getParentJobs(Job)

For creating a graph per view I also need

  • List<View> getAllViews()
  • List<Job>  getJobs(View)

And for keeping the graphs up to date I need to be informed about configuration changes.

  • void onJobConfigChange(Job)

I have to search for these…

I am working on the plugin part now – so I’ll write more if I have finished that.

Explore posts in the same categories: en, Hudson

7 Kommentare - “My first Hudson PlugIn”

  1. kutzi Says:

    Sounds like exactly what I’m just looking for.
    Has there been any progress lately on the plugin?

    • janmaterne Says:

      Sorry, no news. I hadnt any time to work on that, but it’s still on my todo list.
      When finished it will be available via update center and in the Hudson svn repository.

      • kutzi Says:

        Okay.
        BTW: have you seen Hudson.getInstance().getDependencyGraph() ?
        Looks like that could be useful for your plugin.

  2. janmaterne Says:

    No I havent, but I dont have investigated much effort in that. Thanks for the hint – I’ll read the JavaDocs.

  3. darxriggs Says:

    I was also thinking of implementing such a plugin. Please let me know of your progress.

    Another feature I had in mind was to also analyze the dependencies from the below plugin to have the full picture.

    http://wiki.hudson-ci.org//display/HUDSON/Join+Plugin

  4. darxriggs Says:

    I just found out that there is already an experimental dependency graph implementation integrated into Hudson.

    Just add „/dependencyGraph/graph“ to your base Hudson URL (eg. http://localhost:8080/dependencyGraph/graph) and it shows up.


Schreibe einen Kommentar

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s


%d Bloggern gefällt das: