Access Keys:
Skip to content (Access Key - 0)

Dan Hardiker Blog from May 06, 2008

  2008/05/06
Diary of a FishEye Hacker
Last Changed by Dan Hardiker, May 06, 2008 21:48

Well I decided that seen as nobody else outside of Atlassian has yet braved the challenge of FishEye plugin writing, I thought that I would give it a go. Here's a rather crude guide to how I did.

Day 1

Step 1 - Decide on a plugin to write

This is much harder than I originally anticipated ... at least finding a useful one is! Confluence does general information, JIRA does issues/workflows and Bamboo does cause & effect - each of these products can be abused to do various things that they weren't originally intended to.

We've made Confluence a website and JIRA a time manager – Bamboo has also been extended by others into a repository manager which tags & releases successful builds. Unfortunately FishEye is a far more focussed tool - one that was significantly enhanced by Crucible, leaving the scope for extensions somewhat limited; aside from the academic exercise of trying creating plugins.

Anyway, I eventually settled on a reporting plugin which provides a framework for alternative views on the repository data. For example: compare the commit ratio's of the various members of a group of a period of time - by size and by quantity.

Step 2 - Creating the shell

My first POM was essentially my standard Confluence POM but stripped back to to just the servlet-api and junit dependencies - I then manually add in the jar's from the fisheye distribution to enable compilation from within the IDE, however this is far from perfect.

Then Jonathan Nolen pointed me at a more useful one, and then I found another:

Both of these use FishEye 1.4.2, and I'm needing to compile against v1.5.1. After a quick word via Jonathan we now have 1.5.1 here: https://maven.atlassian.com/public/com/atlassian/fisheye/fisheye-jar/1.5.1/

Step 3 - Understand the API

Well, at least try to begin to understand the API so that I can start to produce something more useful than a Hello World! So I went on the hunt for the information that I needed to be able to write a plugin to do anything useful, that is:

  1. Static APIs in the JavaDoc
  2. Documentation about the component architecture (spring) and the components available
  3. Real-world examples of the modules being used
  4. Source to get me going when I needed to delve a little deeper

Unfortunately, the javadoc was sparse to say the least - certainly nothing in there that was any use.

Day 2

Step 4 - Debugging FishEye plugins

If you add the following to the bin/run.sh file it will cause it to listen on port 5005 for a debugger, allowing you to connect your IDE:

export FISHEYE_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005"

Step 5 - Blood, Sweat and Beers

Well, working with FishEye isn't exactly a holiday; in fact it's quite the opposite as it's enough to drive to (more) drink - so to help, here are a few notes that I've got from working with it for a while:

Static APIs & Spring

  1. AppConfig.getsConfig() gets you the RootConfig object which contains the system configuration, including the repository details.
  2. You can get hold of Spring components with the @Autowired annontation, you can also use a standard setter.

There isn't any Java source available, even to commercial licensees - this causes problems when it comes to finding out what you can do. With a good IDE, a reasonable debugger and a large helping of luck/experience you can mitigate this – although you will find your swear jar filling at an alarming rate!

Plugin State Aware
Servlet, RPC & Spring Component modules don't have the StateAware methods called on enable/disable. To be more specific, none of them are called. The best I've found you can get so far, is to call enabled() from the constructor method:

public class MyServlet extends HttpServlet implements StateAware {

	public HelloWorldServlet() {
    	enabled();
	}

	// Implementation of API: StateAware
	
}

This is like building a house of cards I agree, and I appeal to the FishEye developers to have the StateAware interface respected like it is in Confluence.

WebWork & XWork Hacking
I've hacked around through APIs many times before, and David Peterson came up with the Confluence [Conveyor library] which is an elegant way of overriding actions. So my thoughts turned to attempting the following:

  1. Override an action, just to replace the result (and hopefully serve the resulting JSP from inside the plugin).
  2. Create a new action which has the same look and feel as the rest of FishEye
  3. Look at linking it in via the user interface dynamically

Man - what have I let myself in for? ... Well after a day of hacking ... hell! I've had to seriously overhaul the Conveyor library, and effectively turn it inside out. However I now have the ability to add my own actions!

Brilliant ... unfortunately the result files (*.jsp / *.vm) still need to be sourced from the root classloader (i.e. you have to put them in the content directory of the webapp). I spent a few hours digging into hacking up the velocity classloader on the fly, but it just doesn't make sense ... instead you will want to copy Confluence by adding something like this to velocity.properties:

# Plugin subsystem
resource.loader=wwfile,wwclass,confplugin

# dynamic plugin classpath loader (for plugin resources)
confplugin.resource.loader.description=Confluence Dynamic Plugin classpath loader
confplugin.resource.loader.class=com.atlassian.velocity.DynamicPluginResourceLoader

# set caching on for resource loaders (see com.opensymphony.webwork.views.velocity.VelocityManager)
# comment in these lines to add template caching (faster)
wwfile.resource.loader.cache=true
wwclass.resource.loader.cache=true
confplugin.resource.loader.cache=true

The class file will look something like this (although it will need to get the pluginAccessor from Spring another way, as ContainerManager doesn't exist):

public class DynamicPluginResourceLoader extends ResourceLoader
{
    private PluginAccessor pluginAccessor;

    public void init(ExtendedProperties extendedProperties)
    {
        // do nothing
    }

    public InputStream getResourceStream(String name) throws ResourceNotFoundException
    {
        while (name.startsWith("/") && name.length() > 1)
            name = name.substring(1);

        if (pluginAccessor == null)
        {
            pluginAccessor = (PluginAccessor) ContainerManager.getComponent("pluginAccessor");

            if (pluginAccessor == null)
                throw new ResourceNotFoundException("No plugin manager.");
        }

        return pluginAccessor.getDynamicResourceAsStream(name);
    }

    public boolean isSourceModified(Resource resource)
    {
        return false; // copied from org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
    }

    public long getLastModified(Resource resource)
    {
        return 0; // copied from org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
    }
}

I've not dug into what you'd need for JSP / Freemarker result support.

Decoration ... or the lack of
Unfortunately, FishEye doesn't use sitemesh - so decorating up is more than a pain. I've settled for the fact that I'm going to have to mimic the UI and it wont just fit in automatically. Sitemesh will also open the door to theme plugins.

Step 6?

I'm going to try and figure out how the charting stuff works in FishEye and replicate that with my own data ... that should be interesting!

We'll see how that goes.

Posted at 06 May @ 9:29 PM by Dan Hardiker 3 Comments
Toggle Sidebar

Archives

  1. 2010
    1. November
    2. October
  2. 2009
    1. May
    2. April
    3. March
  3. 2008
    1. November
    2. October
    3. August
    4. June
    5. May
    6. April
    7. March

Blogroll

Adaptavist Theme Builder Powered by Atlassian Confluence