You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@taverna.apache.org by st...@apache.org on 2015/02/17 12:45:29 UTC

[05/52] [abbrv] incubator-taverna-workbench git commit: taverna-ui-impl/

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-renderers-impl/src/main/java/net/sf/taverna/t2/renderers/impl/XMLTree.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-renderers-impl/src/main/java/net/sf/taverna/t2/renderers/impl/XMLTree.java b/taverna-workbench-renderers-impl/src/main/java/net/sf/taverna/t2/renderers/impl/XMLTree.java
new file mode 100644
index 0000000..2d53218
--- /dev/null
+++ b/taverna-workbench-renderers-impl/src/main/java/net/sf/taverna/t2/renderers/impl/XMLTree.java
@@ -0,0 +1,329 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *    
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.renderers.impl;
+
+import static java.util.prefs.Preferences.userNodeForPackage;
+import static javax.swing.JFileChooser.APPROVE_OPTION;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.tree.TreeSelectionModel.SINGLE_TREE_SELECTION;
+import static org.jdom.output.Format.getPrettyFormat;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.util.Enumeration;
+import java.util.prefs.Preferences;
+
+import javax.swing.JFileChooser;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.JTree;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+
+import org.jdom.Attribute;
+import org.jdom.Content;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.jdom.Parent;
+import org.jdom.Text;
+import org.jdom.input.SAXBuilder;
+import org.jdom.output.XMLOutputter;
+
+/**
+ * An extension of the {@link JTree} class, constructed with a String of XML and
+ * used to display the XML structure as an interactive tree. Derived from <a
+ * href="http://www.devx.com/gethelpon/10MinuteSolution/16694/0/page/1">original
+ * code by Kyle Gabhart</a> and then subsequently heavily rewritten to move to
+ * JDOM, and moved lots of the setup code to the renderer to cut down
+ * initialisation time. Added text node size limit as well. Displaying large
+ * gene sequences as base64 encoded text in a single node really, <i>really</i>
+ * hurts performance.
+ * 
+ * @author Kyle Gabhart
+ * @author Tom Oinn
+ * @author Kevin Glover
+ * @author Ian Dunlop
+ */
+@SuppressWarnings("serial")
+class XMLTree extends JTree {
+	private class XMLNode extends DefaultMutableTreeNode {
+		public XMLNode(Content userObject) {
+			super(userObject);
+		}
+	}
+
+	int textSizeLimit = 1000;
+	final JFileChooser fc = new JFileChooser();
+	Element rootElement = null;
+
+	/**
+	 * Build a new XMLTree from the supplied String containing XML.
+	 * 
+	 * @param text
+	 * @throws IOException
+	 * @throws JDOMException
+	 */
+	public XMLTree(String text) throws IOException, JDOMException {
+		super();
+		Document document = new SAXBuilder(false).build(new StringReader(text));
+		init(document.getRootElement());
+		revalidate();
+	}
+
+	public String getText() {
+		if (rootElement == null)
+			return "";
+		XMLOutputter xo = new XMLOutputter(getPrettyFormat());
+		return xo.outputString(rootElement);
+	}
+
+	public XMLTree(String text, boolean limit) throws IOException,
+			JDOMException {
+		if (!limit)
+			textSizeLimit = -1;
+		Document document = new SAXBuilder(false).build(new StringReader(text));
+		init(document.getRootElement());
+		revalidate();
+	}
+
+	public XMLTree(Document document) {
+		this(document.getRootElement());
+	}
+	
+	public XMLTree(Element element) {
+		super();
+		init(element);
+		revalidate();
+	}
+
+	private void init(Content content) {
+		rootElement = (Element) content;
+		/*
+		 * Fix for platforms other than metal which can't otherwise cope with
+		 * arbitrary size rows
+		 */
+		setRowHeight(0);
+		getSelectionModel().setSelectionMode(SINGLE_TREE_SELECTION);
+		setShowsRootHandles(true);
+		setEditable(false);
+		setModel(new DefaultTreeModel(createTreeNode(content)));
+		setCellRenderer(new DefaultTreeCellRenderer() {
+			@Override
+			public Color getBackgroundNonSelectionColor() {
+				return null;
+			}
+
+			@Override
+			public Color getBackground() {
+				return null;
+			}
+
+			@Override
+			public Component getTreeCellRendererComponent(JTree tree,
+					Object value, boolean sel, boolean expanded, boolean leaf,
+					int row, boolean hasFocus) {
+				super.getTreeCellRendererComponent(tree, value, sel, expanded,
+						leaf, row, hasFocus);
+				setOpaque(false);
+				if (value instanceof XMLNode) {
+					XMLNode node = (XMLNode) value;
+					if (node.getUserObject() instanceof Element)
+						renderElementNode((Element) node.getUserObject());
+					else if (node.getUserObject() instanceof Text)
+						renderTextNode((Text) node.getUserObject());
+					// TODO what about other node types?
+				}
+				setBackground(new Color(0, 0, 0, 0));
+				return this;
+			}
+
+			private void renderElementNode(Element element) {
+				// setIcon(TavernaIcons.xmlNodeIcon);
+				StringBuilder nameBuffer = new StringBuilder("<html>")
+						.append(element.getQualifiedName());
+				/*
+				 * Bit of a quick and dirty hack here to try to ensure that the
+				 * element namespace is shown. There appears no way to get the
+				 * actual xmlns declarations that are part of an element through
+				 * jdom. Also, please note, there's no namespace handling at all
+				 * for attributes...
+				 */
+				if (element.getParent() instanceof Element) {
+					Element parent = (Element) element.getParent();
+					if (parent.getNamespace(element.getNamespacePrefix()) == null)
+						nameBuffer
+								.append(" <font color=\"purple\">xmlns:")
+								.append(element.getNamespacePrefix())
+								.append("</font>=\"<font color=\"green\">")
+								.append(element.getNamespaceURI() + "</font>\"");
+				} else
+					nameBuffer.append(" <font color=\"purple\">xmlns:")
+							.append(element.getNamespacePrefix())
+							.append("</font>=\"<font color=\"green\">")
+							.append(element.getNamespaceURI() + "</font>\"");
+
+				String sep = "";
+				for (Object a : element.getAttributes()) {
+					Attribute attribute = (Attribute) a;
+					String name = attribute.getName().trim();
+					String value = attribute.getValue().trim();
+					if (value != null && value.length() > 0) {
+						// TODO xml-quote name and value
+						nameBuffer.append(sep)
+								.append(" <font color=\"purple\">")
+								.append(name)
+								.append("</font>=\"<font color=\"green\">")
+								.append(value).append("</font>\"");
+						sep = ",";
+					}
+				}
+
+				nameBuffer.append("</html>");
+				setText(nameBuffer.toString());
+			}
+
+			private void renderTextNode(Text text) {
+				// setIcon(TavernaIcons.leafIcon);
+				String name = text.getText();
+				if (textSizeLimit > -1 && name.length() > textSizeLimit)
+					name = name.substring(0, textSizeLimit) + "...";
+				setText("<html><pre><font color=\"blue\">"
+						+ name.replaceAll("<br>", "\n").replaceAll("<", "&lt;")
+						+ "</font></pre></html>");
+			}
+		});
+		setAllNodesExpanded();
+
+		// Add a listener to present the 'save as text' option
+		addMouseListener(new MouseAdapter() {
+			@Override
+			public void mousePressed(MouseEvent e) {
+				if (e.isPopupTrigger())
+					doEvent(e);
+			}
+
+			@Override
+			public void mouseReleased(MouseEvent e) {
+				if (e.isPopupTrigger())
+					doEvent(e);
+			}
+
+			public void doEvent(MouseEvent e) {
+				JPopupMenu menu = new JPopupMenu();
+				JMenuItem item = new JMenuItem("Save as XML text");
+				menu.add(item);
+				item.addActionListener(new ActionListener() {
+					@Override
+					public void actionPerformed(ActionEvent ae) {
+						saveTreeXML();
+					}
+				});
+				menu.show(XMLTree.this, e.getX(), e.getY());
+			}
+		});
+	}
+
+	private void saveTreeXML() {
+		try {
+			Preferences prefs = userNodeForPackage(XMLTree.class);
+			String curDir = prefs.get("currentDir",
+					System.getProperty("user.home"));
+			fc.resetChoosableFileFilters();
+			fc.setFileFilter(new ExtensionFileFilter(new String[] { "xml" }));
+			fc.setCurrentDirectory(new File(curDir));
+			if (fc.showSaveDialog(this) == APPROVE_OPTION) {
+				prefs.put("currentDir", fc.getCurrentDirectory().toString());
+				saveTreeXML(fc.getSelectedFile());
+			}
+		} catch (Exception ex) {
+			showMessageDialog(this, "Problem saving XML:\n" + ex.getMessage(),
+					"Error!", ERROR_MESSAGE);
+		}
+	}
+
+	private void saveTreeXML(File file) throws IOException {
+		try (PrintWriter out = new PrintWriter(new FileWriter(file))) {
+			out.print(this.getText());
+		}
+	}
+
+	public void setAllNodesExpanded() {
+		synchronized (this.getModel()) {
+			expandAll(this, new TreePath(this.getModel().getRoot()), true);
+		}
+	}
+
+	private void expandAll(JTree tree, TreePath parent, boolean expand) {
+		synchronized (this.getModel()) {
+			/*
+			 * Traverse children
+			 * 
+			 * Ignores nodes who's userObject is a Processor type to avoid
+			 * overloading the UI with nodes at startup.
+			 */
+			TreeNode node = (TreeNode) parent.getLastPathComponent();
+			for (Enumeration<?> e = node.children(); e.hasMoreElements(); ) {
+				TreeNode n = (TreeNode) e.nextElement();
+				expandAll(tree, parent.pathByAddingChild(n), expand);
+			}
+			// Expansion or collapse must be done bottom-up
+			if (expand)
+				tree.expandPath(parent);
+			else
+				tree.collapsePath(parent);
+		}
+	}
+
+	public void setTextNodeSizeLimit(int sizeLimit) {
+		textSizeLimit = sizeLimit;
+	}
+
+	private XMLNode createTreeNode(Content content) {
+		XMLNode node = new XMLNode(content);
+		if (content instanceof Parent) {
+			Parent parent = (Parent) content;
+			for (Object child : parent.getContent()) {
+				if (child instanceof Element)
+					node.add(createTreeNode((Content) child));
+				else if (textSizeLimit != 0 && child instanceof Text) {
+					Text text = (Text) child;
+					if (!text.getTextNormalize().isEmpty())
+						node.add(createTreeNode(text));
+				}
+			}
+		}
+		return node;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-renderers-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.renderers.Renderer
----------------------------------------------------------------------
diff --git a/taverna-workbench-renderers-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.renderers.Renderer b/taverna-workbench-renderers-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.renderers.Renderer
new file mode 100644
index 0000000..044a396
--- /dev/null
+++ b/taverna-workbench-renderers-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.renderers.Renderer
@@ -0,0 +1,7 @@
+net.sf.taverna.t2.renderers.impl.AdvancedImageRenderer
+net.sf.taverna.t2.renderers.impl.HexBinaryRenderer
+net.sf.taverna.t2.renderers.impl.TextRenderer
+net.sf.taverna.t2.renderers.impl.TextRtfRenderer
+net.sf.taverna.t2.renderers.impl.TextXMLRenderer
+
+

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-renderers-impl/src/main/resources/META-INF/spring/renderers-impl-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-workbench-renderers-impl/src/main/resources/META-INF/spring/renderers-impl-context-osgi.xml b/taverna-workbench-renderers-impl/src/main/resources/META-INF/spring/renderers-impl-context-osgi.xml
new file mode 100644
index 0000000..436426a
--- /dev/null
+++ b/taverna-workbench-renderers-impl/src/main/resources/META-INF/spring/renderers-impl-context-osgi.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:beans="http://www.springframework.org/schema/beans"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans.xsd
+                      http://www.springframework.org/schema/osgi
+                      http://www.springframework.org/schema/osgi/spring-osgi.xsd">
+
+	<service ref="AdvancedImageRenderer" interface="net.sf.taverna.t2.renderers.Renderer" />
+	<service ref="HexBinaryRenderer" interface="net.sf.taverna.t2.renderers.Renderer" />
+	<service ref="TextRenderer" interface="net.sf.taverna.t2.renderers.Renderer" />
+	<service ref="TextRtfRenderer" interface="net.sf.taverna.t2.renderers.Renderer" />
+	<service ref="TextXMLRenderer" interface="net.sf.taverna.t2.renderers.Renderer" />
+
+	<service ref="RendererRegistry" interface="net.sf.taverna.t2.renderers.RendererRegistry" />
+
+	<list id="renderers" interface="net.sf.taverna.t2.renderers.Renderer" cardinality="0..N" />
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-renderers-impl/src/main/resources/META-INF/spring/renderers-impl-context.xml
----------------------------------------------------------------------
diff --git a/taverna-workbench-renderers-impl/src/main/resources/META-INF/spring/renderers-impl-context.xml b/taverna-workbench-renderers-impl/src/main/resources/META-INF/spring/renderers-impl-context.xml
new file mode 100644
index 0000000..a041c98
--- /dev/null
+++ b/taverna-workbench-renderers-impl/src/main/resources/META-INF/spring/renderers-impl-context.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<bean id="AdvancedImageRenderer" class="net.sf.taverna.t2.renderers.impl.AdvancedImageRenderer" />
+	<bean id="HexBinaryRenderer" class="net.sf.taverna.t2.renderers.impl.HexBinaryRenderer" />
+	<bean id="TextRenderer" class="net.sf.taverna.t2.renderers.impl.TextRenderer" />
+	<bean id="TextRtfRenderer" class="net.sf.taverna.t2.renderers.impl.TextRtfRenderer" />
+	<bean id="TextXMLRenderer" class="net.sf.taverna.t2.renderers.impl.TextXMLRenderer" />
+
+	<bean id="RendererRegistry" class="net.sf.taverna.t2.renderers.impl.RendererRegistryImpl">
+			<property name="renderers" ref="renderers" />
+	</bean>
+
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-renderers-impl/src/test/java/net/sf/taverna/t2/renderers/TestRendererSPI.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-renderers-impl/src/test/java/net/sf/taverna/t2/renderers/TestRendererSPI.java b/taverna-workbench-renderers-impl/src/test/java/net/sf/taverna/t2/renderers/TestRendererSPI.java
new file mode 100644
index 0000000..ece7cf5
--- /dev/null
+++ b/taverna-workbench-renderers-impl/src/test/java/net/sf/taverna/t2/renderers/TestRendererSPI.java
@@ -0,0 +1,154 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *    
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.renderers;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashSet;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+
+@SuppressWarnings("unused")
+public class TestRendererSPI {
+	
+	private static final String TEST_NS = "testNS";
+	
+	@Test
+	public void doNothing() {
+		//do nothing for the moment
+	}
+	
+//	@Test
+//	public void getAllRenderers() {
+//		RendererRegistry rendererRegistry = new RendererRegistry();
+//		assertEquals(rendererRegistry.getInstances().size(), 10);
+//	}
+//	
+//	@Test
+//	public void checkTextHtmlMimeType() throws EmptyListException, MalformedListException, UnsupportedObjectTypeException {
+//		String mimeType = "text/html";
+//		String html = "<HTML><HEAD></HEAD><BODY>hello</BODY></HTML>";
+//		EntityIdentifier entityIdentifier = facade.register(html, "utf-8");
+//		RendererRegistry rendererRegistry = new RendererRegistry();
+//		List<Renderer> renderersForMimeType = rendererRegistry.getRenderersForMimeType(facade, entityIdentifier, mimeType);
+//		assertEquals(renderersForMimeType.size(),2);
+//		assertEquals(renderersForMimeType.get(0).getClass().getSimpleName(), "TextRenderer");
+//		assertEquals(renderersForMimeType.get(1).getClass().getSimpleName(), "TextHtmlRenderer");
+//		assertTrue(renderersForMimeType.get(0).canHandle("text/html"));
+//	}
+//	
+//	@Test
+//	public void checkURLMimeType() throws EmptyListException, MalformedListException, UnsupportedObjectTypeException {
+//		String mimeType = "text/x-taverna-web-url.text";
+//		String url = "http://google.com";
+//		EntityIdentifier entityIdentifier = facade.register(url);
+//		RendererRegistry rendererRegistry = new RendererRegistry();
+//		List<Renderer> renderersForMimeType = rendererRegistry.getRenderersForMimeType(facade, entityIdentifier, mimeType);
+//		assertEquals(renderersForMimeType.size(),2);
+//		assertEquals(renderersForMimeType.get(0).getClass().getSimpleName(), "TextRenderer");
+//		assertEquals(renderersForMimeType.get(1).getClass().getSimpleName(), "TextTavernaWebUrlRenderer");
+//		assertTrue(renderersForMimeType.get(1).canHandle("text/x-taverna-web-url.text"));
+//	}
+//	
+//	@Test
+//	public void checkJMolMimeType() throws EmptyListException, MalformedListException, UnsupportedObjectTypeException {
+//		String mimeType ="chemical/x-pdb";
+//		String jmol = "jmol";
+//		EntityIdentifier entityIdentifier = facade.register(jmol);
+//		RendererRegistry rendererRegistry = new RendererRegistry();
+//		List<Renderer> renderersForMimeType = rendererRegistry.getRenderersForMimeType(facade, entityIdentifier, mimeType);
+//		assertEquals(renderersForMimeType.size(), 1);
+//		assertEquals(renderersForMimeType.get(0).getClass().getSimpleName(), "JMolRenderer");
+//		assertTrue(renderersForMimeType.get(0).canHandle("chemical/x-mdl-molfile"));
+//		assertTrue(renderersForMimeType.get(0).canHandle("chemical/x-cml"));
+//	}
+//	
+//	@Test
+//	public void checkSeqVistaMimeType() throws EmptyListException, MalformedListException, UnsupportedObjectTypeException {
+//		String mimeType ="chemical/x-swissprot";
+//		String type = "seqvista";
+//		EntityIdentifier entityIdentifier = facade.register(type);
+//		RendererRegistry rendererRegistry = new RendererRegistry();
+//		List<Renderer> renderersForMimeType = rendererRegistry.getRenderersForMimeType(facade, entityIdentifier, mimeType);
+//		assertEquals(renderersForMimeType.size(), 1);
+//		assertEquals(renderersForMimeType.get(0).getClass().getSimpleName(), "SeqVistaRenderer");
+//		assertTrue(renderersForMimeType.get(0).canHandle("chemical/x-embl-dl-nucleotide"));
+//		assertTrue(renderersForMimeType.get(0).canHandle("chemical/x-fasta"));
+//	}
+//	
+//	@Test
+//	public void checkSVGMimeType() throws EmptyListException, MalformedListException, UnsupportedObjectTypeException {
+//		String mimeType ="image/svg+xml";
+//		String type = "SVG";
+//		EntityIdentifier entityIdentifier = facade.register(type);
+//		RendererRegistry rendererRegistry = new RendererRegistry();
+//		List<Renderer> renderersForMimeType = rendererRegistry.getRenderersForMimeType(facade, entityIdentifier, mimeType);
+//		assertEquals(renderersForMimeType.size(), 2);
+//		assertEquals(renderersForMimeType.get(1).getClass().getSimpleName(), "SVGRenderer");
+//	}
+//	
+//	@Test
+//	public void checkTextMimeType() throws EmptyListException, MalformedListException, UnsupportedObjectTypeException {
+//		String mimeType ="text/text";
+//		String type = "text";
+//		EntityIdentifier entityIdentifier = facade.register(type);
+//		RendererRegistry rendererRegistry = new RendererRegistry();
+//		List<Renderer> renderersForMimeType = rendererRegistry.getRenderersForMimeType(facade, entityIdentifier, mimeType);
+//		assertEquals(renderersForMimeType.size(), 1);
+//		assertEquals(renderersForMimeType.get(0).getClass().getSimpleName(), "TextRenderer");
+//	}
+//	
+//	@Test
+//	public void checkTextRtfMimeType() throws EmptyListException, MalformedListException, UnsupportedObjectTypeException {
+//		String mimeType ="text/rtf";
+//		String type = "textRTF";
+//		EntityIdentifier entityIdentifier = facade.register(type);
+//		RendererRegistry rendererRegistry = new RendererRegistry();
+//		List<Renderer> renderersForMimeType = rendererRegistry.getRenderersForMimeType(facade, entityIdentifier, mimeType);
+//		assertEquals(renderersForMimeType.size(), 2);
+//		assertEquals(renderersForMimeType.get(1).getClass().getSimpleName(), "TextRtfRenderer");
+//	}
+//	
+//	@Test
+//	public void checkTextXMLMimeType() throws EmptyListException, MalformedListException, UnsupportedObjectTypeException {
+//		String mimeType ="text/xml";
+//		String type = "textXML";
+//		EntityIdentifier entityIdentifier = facade.register(type);
+//		RendererRegistry rendererRegistry = new RendererRegistry();
+//		List<Renderer> renderersForMimeType = rendererRegistry.getRenderersForMimeType(facade, entityIdentifier, mimeType);
+//		assertEquals(renderersForMimeType.size(), 2);
+//		assertEquals(renderersForMimeType.get(1).getClass().getSimpleName(), "TextXMLRenderer");
+//	}
+//	
+//	@Before
+//	public void setDataManager() {
+//		// dManager = new FileDataManager("testNS",
+//		// new HashSet<LocationalContext>(), new File("/tmp/fish"));
+//		dManager = new InMemoryDataManager(TEST_NS,
+//				new HashSet<LocationalContext>());
+//		facade = new DataFacade(dManager);
+//	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-report-impl/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-workbench-report-impl/pom.xml b/taverna-workbench-report-impl/pom.xml
new file mode 100644
index 0000000..c0e72bb
--- /dev/null
+++ b/taverna-workbench-report-impl/pom.xml
@@ -0,0 +1,40 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<artifactId>ui-impl</artifactId>
+		<groupId>net.sf.taverna.t2</groupId>
+		<version>2.0-SNAPSHOT</version>
+	</parent>
+	<groupId>net.sf.taverna.t2.ui-impl</groupId>
+	<artifactId>report-impl</artifactId>
+	<packaging>bundle</packaging>
+	<name>Reporting Implementation</name>
+	<dependencies>
+		<dependency>
+			<groupId>net.sf.taverna.t2.core</groupId>
+			<artifactId>workflowmodel-api</artifactId>
+			<version>${t2.core.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>net.sf.taverna.t2.ui-api</groupId>
+			<artifactId>report-api</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>net.sf.taverna.t2.ui-api</groupId>
+			<artifactId>file-api</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>net.sf.taverna.t2.ui-api</groupId>
+			<artifactId>edits-api</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>uk.org.taverna.configuration</groupId>
+			<artifactId>taverna-configuration-api</artifactId>
+			<version>${taverna.configuration.version}</version>
+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-report-impl/src/main/java/net/sf/taverna/t2/workbench/report/config/impl/ReportManagerConfigurationImpl.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-report-impl/src/main/java/net/sf/taverna/t2/workbench/report/config/impl/ReportManagerConfigurationImpl.java b/taverna-workbench-report-impl/src/main/java/net/sf/taverna/t2/workbench/report/config/impl/ReportManagerConfigurationImpl.java
new file mode 100644
index 0000000..b5986b5
--- /dev/null
+++ b/taverna-workbench-report-impl/src/main/java/net/sf/taverna/t2/workbench/report/config/impl/ReportManagerConfigurationImpl.java
@@ -0,0 +1,71 @@
+/**
+ *
+ */
+package net.sf.taverna.t2.workbench.report.config.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import uk.org.taverna.configuration.AbstractConfigurable;
+import uk.org.taverna.configuration.ConfigurationManager;
+
+import net.sf.taverna.t2.workbench.report.config.ReportManagerConfiguration;
+import net.sf.taverna.t2.workflowmodel.health.RemoteHealthChecker;
+
+/**
+ * @author alanrw
+ *
+ */
+public final class ReportManagerConfigurationImpl extends AbstractConfigurable implements ReportManagerConfiguration {
+
+	private static final int DEFAULT_TIMEOUT = 10;
+
+	private Map<String, String> defaultPropertyMap;
+
+	public ReportManagerConfigurationImpl(ConfigurationManager configurationManager) {
+		super(configurationManager);
+	}
+
+    public String getCategory() {
+        return "general";
+    }
+
+    public Map<String, String> getDefaultPropertyMap() {
+
+        if (defaultPropertyMap == null) {
+            defaultPropertyMap = new HashMap<String, String>();
+            defaultPropertyMap.put(TIMEOUT, Integer.toString(DEFAULT_TIMEOUT));
+            defaultPropertyMap.put(ON_EDIT, QUICK_CHECK);
+            defaultPropertyMap.put(ON_OPEN, QUICK_CHECK);
+            defaultPropertyMap.put(BEFORE_RUN, FULL_CHECK);
+            defaultPropertyMap.put(QUERY_BEFORE_RUN, ERRORS_OR_WARNINGS);
+            defaultPropertyMap.put(REPORT_EXPIRATION, Integer.toString(DEFAULT_REPORT_EXPIRATION));
+        }
+        return defaultPropertyMap;
+    }
+
+    public String getDisplayName() {
+        return "Validation report";
+    }
+
+    public String getFilePrefix() {
+        return "ReportManager";
+    }
+
+	public String getUUID() {
+		return "F86378E5-0EC4-4DE9-8A55-6098595413DC";
+	}
+
+	@Override
+	public void applySettings() {
+		RemoteHealthChecker.setTimeoutInSeconds(Integer.parseInt(this.getProperty(TIMEOUT)));
+	}
+
+	public void setProperty(String key, String value) {
+		super.setProperty(key, value);
+		if (key.equals(TIMEOUT)) {
+			applySettings();
+		}
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-report-impl/src/main/java/net/sf/taverna/t2/workbench/report/impl/ReportManagerImpl.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-report-impl/src/main/java/net/sf/taverna/t2/workbench/report/impl/ReportManagerImpl.java b/taverna-workbench-report-impl/src/main/java/net/sf/taverna/t2/workbench/report/impl/ReportManagerImpl.java
new file mode 100644
index 0000000..825efee
--- /dev/null
+++ b/taverna-workbench-report-impl/src/main/java/net/sf/taverna/t2/workbench/report/impl/ReportManagerImpl.java
@@ -0,0 +1,564 @@
+/**
+ *
+ */
+package net.sf.taverna.t2.workbench.report.impl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+import net.sf.taverna.t2.lang.observer.MultiCaster;
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.visit.HierarchyTraverser;
+import net.sf.taverna.t2.visit.VisitKind;
+import net.sf.taverna.t2.visit.VisitReport;
+import net.sf.taverna.t2.visit.VisitReport.Status;
+import net.sf.taverna.t2.visit.Visitor;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.edits.EditManager.AbstractDataflowEditEvent;
+import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.events.ClosedDataflowEvent;
+import net.sf.taverna.t2.workbench.file.events.FileManagerEvent;
+import net.sf.taverna.t2.workbench.file.events.SetCurrentDataflowEvent;
+import net.sf.taverna.t2.workbench.report.DataflowReportEvent;
+import net.sf.taverna.t2.workbench.report.FailedEntityKind;
+import net.sf.taverna.t2.workbench.report.IncompleteDataflowKind;
+import net.sf.taverna.t2.workbench.report.InvalidDataflowKind;
+import net.sf.taverna.t2.workbench.report.ReportManager;
+import net.sf.taverna.t2.workbench.report.ReportManagerEvent;
+import net.sf.taverna.t2.workbench.report.UnresolvedOutputKind;
+import net.sf.taverna.t2.workbench.report.UnsatisfiedEntityKind;
+import net.sf.taverna.t2.workbench.report.config.ReportManagerConfiguration;
+import net.sf.taverna.t2.workflowmodel.Dataflow;
+import net.sf.taverna.t2.workflowmodel.DataflowValidationReport;
+import net.sf.taverna.t2.workflowmodel.Processor;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author Alan R Williams
+ *
+ */
+public class ReportManagerImpl implements Observable<ReportManagerEvent>, ReportManager {
+
+	private static final long MAX_AGE_OUTDATED_MILLIS = 1 * 60 * 60 * 1000; // 1 hour
+	private static Logger logger = Logger.getLogger(ReportManagerImpl.class);
+
+	private ReportManagerConfiguration reportManagerConfiguration;
+	private HierarchyTraverser traverser = null;
+	private Map<Dataflow, Map<Object, Set<VisitReport>>> reportMap = Collections
+			.synchronizedMap(new WeakHashMap<Dataflow, Map<Object, Set<VisitReport>>>());;
+	private Map<Dataflow, Map<Object, Status>> statusMap = Collections
+			.synchronizedMap(new WeakHashMap<Dataflow, Map<Object, Status>>());
+	private Map<Dataflow, Map<Object, String>> summaryMap = Collections
+			.synchronizedMap(new WeakHashMap<Dataflow, Map<Object, String>>());
+	private Map<Dataflow, Long> lastCheckedMap = Collections
+			.synchronizedMap(new WeakHashMap<Dataflow, Long>());
+	private Map<Dataflow, Long> lastFullCheckedMap = Collections
+			.synchronizedMap(new WeakHashMap<Dataflow, Long>());
+	private Map<Dataflow, String> lastFullCheckedDataflowIdMap = Collections
+			.synchronizedMap(new WeakHashMap<Dataflow, String>());
+
+	private EditManager editManager;
+	private FileManager fileManager;
+
+	// private Set<Visitor<?>> visitors;
+
+	protected ReportManagerImpl(EditManager editManager, FileManager fileManager,
+			Set<Visitor<?>> visitors, ReportManagerConfiguration reportManagerConfiguration)
+			throws IllegalStateException {
+		this.editManager = editManager;
+		this.fileManager = fileManager;
+		this.reportManagerConfiguration = reportManagerConfiguration;
+		// this.visitors = visitors;
+		traverser = new HierarchyTraverser(visitors);
+		ReportManagerFileObserver fileObserver = new ReportManagerFileObserver();
+		fileManager.addObserver(fileObserver);
+		addEditObserver();
+		reportManagerConfiguration.applySettings();
+	}
+
+	private void addEditObserver() {
+		synchronized (editManager) {
+			List<Observer<EditManagerEvent>> currentObservers = editManager.getObservers();
+			for (Observer<EditManagerEvent> o : currentObservers) {
+				editManager.removeObserver(o);
+			}
+			editManager.addObserver(new ReportManagerEditObserver());
+			for (Observer<EditManagerEvent> o : currentObservers) {
+				editManager.addObserver(o);
+			}
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see
+	 * net.sf.taverna.t2.workbench.report.ReportManagerI#updateReport(net.sf.taverna.t2.workflowmodel
+	 * .Dataflow, boolean, boolean)
+	 */
+	@Override
+	public void updateReport(Dataflow d, boolean includeTimeConsuming, boolean remember) {
+		Set<VisitReport> oldTimeConsumingReports = null;
+		long time = System.currentTimeMillis();
+		long expiration = Integer.parseInt(reportManagerConfiguration
+				.getProperty(ReportManagerConfiguration.REPORT_EXPIRATION)) * 60 * 1000;
+		if (remember && !includeTimeConsuming
+				&& ((expiration == 0) || ((time - getLastFullCheckedTime(d)) < expiration))) {
+			oldTimeConsumingReports = getTimeConsumingReports(d);
+		}
+		Map<Object, Set<VisitReport>> reportsEntry = new HashMap<Object, Set<VisitReport>>();
+		Map<Object, Status> statusEntry = new HashMap<Object, Status>();
+		Map<Object, String> summaryEntry = new HashMap<Object, String>();
+		reportMap.put(d, reportsEntry);
+		statusMap.put(d, statusEntry);
+		summaryMap.put(d, summaryEntry);
+		validateDataflow(d, reportsEntry, statusEntry, summaryEntry);
+
+		Set<VisitReport> newReports = new HashSet<VisitReport>();
+		traverser.traverse(d, new ArrayList<Object>(), newReports, includeTimeConsuming);
+		for (VisitReport vr : newReports) {
+			addReport(reportsEntry, statusEntry, summaryEntry, vr);
+		}
+		if (oldTimeConsumingReports != null) {
+			for (VisitReport vr : oldTimeConsumingReports) {
+				addReport(reportsEntry, statusEntry, summaryEntry, vr);
+			}
+		}
+		time = System.currentTimeMillis();
+		lastCheckedMap.put(d, time);
+		if (includeTimeConsuming) {
+			lastFullCheckedMap.put(d, time);
+			lastFullCheckedDataflowIdMap.put(d, d.getIdentifier());
+		}
+		multiCaster.notify(new DataflowReportEvent(d));
+	}
+
+	private void updateObjectReportInternal(Dataflow d, Object o) {
+		Map<Object, Set<VisitReport>> reportsEntry = reportMap.get(d);
+		Map<Object, Status> statusEntry = statusMap.get(d);
+		Map<Object, String> summaryEntry = summaryMap.get(d);
+		if (reportsEntry == null) {
+			logger.error("Attempt to update reports on an object in a dataflow that has not been checked");
+			reportsEntry = new HashMap<Object, Set<VisitReport>>();
+			statusEntry = new HashMap<Object, Status>();
+			summaryEntry = new HashMap<Object, String>();
+			reportMap.put(d, reportsEntry);
+			statusMap.put(d, statusEntry);
+			summaryMap.put(d, summaryEntry);
+		} else {
+			reportsEntry.remove(o);
+			statusEntry.remove(o);
+			summaryEntry.remove(o);
+		}
+		// Assume o is directly inside d
+		List<Object> ancestry = new ArrayList<Object>();
+		ancestry.add(d);
+		Set<VisitReport> newReports = new HashSet<VisitReport>();
+		traverser.traverse(o, ancestry, newReports, true);
+		for (VisitReport vr : newReports) {
+			addReport(reportsEntry, statusEntry, summaryEntry, vr);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see
+	 * net.sf.taverna.t2.workbench.report.ReportManagerI#updateObjectSetReport(net.sf.taverna.t2
+	 * .workflowmodel.Dataflow, java.util.Set)
+	 */
+	@Override
+	public void updateObjectSetReport(Dataflow d, Set<Object> objects) {
+		for (Object o : objects) {
+			updateObjectReportInternal(d, o);
+		}
+		multiCaster.notify(new DataflowReportEvent(d));
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see net.sf.taverna.t2.workbench.report.ReportManagerI#updateObjectReport(net.sf.taverna.t2.
+	 * workflowmodel.Dataflow, java.lang.Object)
+	 */
+	@Override
+	public void updateObjectReport(Dataflow d, Object o) {
+		updateObjectReportInternal(d, o);
+		multiCaster.notify(new DataflowReportEvent(d));
+	}
+
+	private Set<VisitReport> getTimeConsumingReports(Dataflow d) {
+		Set<VisitReport> result = new HashSet<VisitReport>();
+		Map<Object, Set<VisitReport>> currentReports = getReports(d);
+		if (currentReports != null) {
+			for (Object o : currentReports.keySet()) {
+				for (VisitReport vr : currentReports.get(o)) {
+					if (vr.wasTimeConsuming()) {
+						result.add(vr);
+					}
+				}
+			}
+		}
+		return result;
+	}
+
+	private void removeReport(Dataflow d) {
+		reportMap.remove(d);
+		statusMap.remove(d);
+		summaryMap.remove(d);
+	}
+
+	private void addReport(Map<Object, Set<VisitReport>> reports, Map<Object, Status> statusEntry,
+			Map<Object, String> summaryEntry, VisitReport newReport) {
+		if (newReport.getCheckTime() == 0) {
+			newReport.setCheckTime(System.currentTimeMillis());
+		}
+		Object subject = newReport.getSubject();
+		Set<VisitReport> currentReports = reports.get(subject);
+		Status newReportStatus = newReport.getStatus();
+		if (currentReports == null) {
+			currentReports = new HashSet<VisitReport>();
+			reports.put(subject, currentReports);
+			statusEntry.put(subject, newReportStatus);
+			summaryEntry.put(subject, newReport.getMessage());
+		} else {
+			Status currentStatus = statusEntry.get(subject);
+			if (currentStatus.compareTo(newReportStatus) < 0) {
+				statusEntry.put(subject, newReportStatus);
+				summaryEntry.put(subject, newReport.getMessage());
+			} else if (currentStatus.compareTo(newReportStatus) == 0) {
+				if (currentStatus.equals(Status.WARNING)) {
+					summaryEntry.put(subject, "Multiple warnings");
+				} else if (currentStatus.equals(Status.SEVERE)) {
+					summaryEntry.put(subject, "Multiple errors");
+				}
+			}
+		}
+		currentReports.add(newReport);
+	}
+
+	private void validateDataflow(Dataflow d, Map<Object, Set<VisitReport>> reportsEntry,
+			Map<Object, Status> statusEntry, Map<Object, String> summaryEntry) {
+		DataflowValidationReport validationReport = d.checkValidity();
+		if (validationReport.isWorkflowIncomplete()) {
+			addReport(reportsEntry, statusEntry, summaryEntry, new VisitReport(
+					IncompleteDataflowKind.getInstance(), d, "Incomplete workflow",
+					IncompleteDataflowKind.INCOMPLETE_DATAFLOW, VisitReport.Status.SEVERE));
+		} else if (!validationReport.isValid()) {
+			addReport(reportsEntry, statusEntry, summaryEntry,
+					new VisitReport(InvalidDataflowKind.getInstance(), d, "Invalid workflow",
+							InvalidDataflowKind.INVALID_DATAFLOW, VisitReport.Status.SEVERE));
+		}
+		fillInReport(reportsEntry, statusEntry, summaryEntry, validationReport);
+	}
+
+	private void fillInReport(Map<Object, Set<VisitReport>> reportsEntry,
+			Map<Object, Status> statusEntry, Map<Object, String> summaryEntry,
+			DataflowValidationReport report) {
+		for (Object o : report.getUnresolvedOutputs()) {
+			addReport(reportsEntry, statusEntry, summaryEntry,
+					new VisitReport(UnresolvedOutputKind.getInstance(), o,
+							"Invalid workflow output", UnresolvedOutputKind.OUTPUT,
+							VisitReport.Status.SEVERE));
+		}
+		for (Object o : report.getFailedEntities()) {
+			addReport(reportsEntry, statusEntry, summaryEntry,
+					new VisitReport(FailedEntityKind.getInstance(), o,
+							"Mismatch of input list depths", FailedEntityKind.FAILED_ENTITY,
+							VisitReport.Status.SEVERE));
+		}
+		for (Object o : report.getUnsatisfiedEntities()) {
+			addReport(reportsEntry, statusEntry, summaryEntry, new VisitReport(
+					UnsatisfiedEntityKind.getInstance(), o, "Unknown prior list depth",
+					UnsatisfiedEntityKind.UNSATISFIED_ENTITY, VisitReport.Status.SEVERE));
+		}
+		// for (DataflowValidationReport subReport : report.getInvalidDataflows().values()) {
+		// fillInReport(descriptionMap, subReport);
+		// }
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see
+	 * net.sf.taverna.t2.workbench.report.ReportManagerI#getReports(net.sf.taverna.t2.workflowmodel
+	 * .Dataflow, java.lang.Object)
+	 */
+	@Override
+	public Set<VisitReport> getReports(Dataflow d, Object object) {
+		Set<VisitReport> result = new HashSet<VisitReport>();
+		Map<Object, Set<VisitReport>> objectReports = reportMap.get(d);
+		if (objectReports != null) {
+			Set<Object> objects = new HashSet<Object>();
+			objects.add(object);
+			if (object instanceof Processor) {
+				objects.addAll(((Processor) object).getActivityList());
+			}
+			for (Object o : objects) {
+				if (objectReports.containsKey(o)) {
+					result.addAll(objectReports.get(o));
+				}
+			}
+		}
+		return result;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see
+	 * net.sf.taverna.t2.workbench.report.ReportManagerI#getReports(net.sf.taverna.t2.workflowmodel
+	 * .Dataflow)
+	 */
+	@Override
+	public Map<Object, Set<VisitReport>> getReports(Dataflow d) {
+		return reportMap.get(d);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see net.sf.taverna.t2.workbench.report.ReportManagerI#isStructurallySound(net.sf.taverna.t2.
+	 * workflowmodel.Dataflow)
+	 */
+	@Override
+	public boolean isStructurallySound(Dataflow d) {
+		Map<Object, Set<VisitReport>> objectReports = reportMap.get(d);
+		if (objectReports == null) {
+			return false;
+		}
+		for (Set<VisitReport> visitReportSet : objectReports.values()) {
+			for (VisitReport vr : visitReportSet) {
+				if (vr.getStatus().equals(Status.SEVERE)) {
+					VisitKind vk = vr.getKind();
+					if ((vk instanceof IncompleteDataflowKind)
+							|| (vk instanceof InvalidDataflowKind)
+							|| (vk instanceof UnresolvedOutputKind)
+							|| (vk instanceof FailedEntityKind)
+							|| (vk instanceof UnsatisfiedEntityKind)) {
+						return false;
+					}
+				}
+			}
+		}
+		return true;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see
+	 * net.sf.taverna.t2.workbench.report.ReportManagerI#getStatus(net.sf.taverna.t2.workflowmodel
+	 * .Dataflow)
+	 */
+	@Override
+	public Status getStatus(Dataflow d) {
+		Map<Object, Set<VisitReport>> objectReports = reportMap.get(d);
+		if (objectReports == null) {
+			return Status.OK;
+		}
+		Status currentStatus = Status.OK;
+		for (Set<VisitReport> visitReportSet : objectReports.values()) {
+			for (VisitReport vr : visitReportSet) {
+				Status status = vr.getStatus();
+				if (status.compareTo(currentStatus) > 0) {
+					currentStatus = status;
+				}
+				if (currentStatus.equals(Status.SEVERE)) {
+					return currentStatus;
+				}
+			}
+		}
+		return currentStatus;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see
+	 * net.sf.taverna.t2.workbench.report.ReportManagerI#getStatus(net.sf.taverna.t2.workflowmodel
+	 * .Dataflow, java.lang.Object)
+	 */
+	@Override
+	public Status getStatus(Dataflow d, Object object) {
+		Status result = Status.OK;
+		Map<Object, Status> statusEntry = statusMap.get(d);
+		if (statusEntry != null) {
+			Status value = statusEntry.get(object);
+			if (value != null) {
+				result = value;
+			}
+		}
+		return result;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see net.sf.taverna.t2.workbench.report.ReportManagerI#getSummaryMessage(net.sf.taverna.t2.
+	 * workflowmodel.Dataflow, java.lang.Object)
+	 */
+	@Override
+	public String getSummaryMessage(Dataflow d, Object object) {
+		String result = null;
+		if (!getStatus(d, object).equals(Status.OK)) {
+			Map<Object, String> summaryEntry = summaryMap.get(d);
+			if (summaryEntry != null) {
+				result = summaryEntry.get(object);
+			}
+		}
+		return result;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see net.sf.taverna.t2.workbench.report.ReportManagerI#getLastCheckedTime(net.sf.taverna.t2.
+	 * workflowmodel.Dataflow)
+	 */
+	@Override
+	public long getLastCheckedTime(Dataflow d) {
+		Long l = lastCheckedMap.get(d);
+		if (l == null) {
+			return 0;
+		} else {
+			return l.longValue();
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see
+	 * net.sf.taverna.t2.workbench.report.ReportManagerI#getLastFullCheckedTime(net.sf.taverna.t2
+	 * .workflowmodel.Dataflow)
+	 */
+	@Override
+	public long getLastFullCheckedTime(Dataflow d) {
+		Long l = lastFullCheckedMap.get(d);
+		if (l == null) {
+			return 0;
+		} else {
+			return l.longValue();
+		}
+	}
+
+	/**
+	 * @author alanrw
+	 *
+	 */
+	public class ReportManagerFileObserver implements Observer<FileManagerEvent> {
+
+		public void notify(Observable<FileManagerEvent> sender, FileManagerEvent message)
+				throws Exception {
+			String onOpen = reportManagerConfiguration.getProperty(
+					ReportManagerConfiguration.ON_OPEN);
+			if (message instanceof ClosedDataflowEvent) {
+				ReportManagerImpl.this.removeReport(((ClosedDataflowEvent) message).getDataflow());
+			} else if (message instanceof SetCurrentDataflowEvent) {
+				Dataflow dataflow = ((SetCurrentDataflowEvent) message).getDataflow();
+				if (!reportMap.containsKey(dataflow)) {
+					if (!onOpen.equals(ReportManagerConfiguration.NO_CHECK)) {
+						updateReport(dataflow,
+								onOpen.equals(ReportManagerConfiguration.FULL_CHECK), true);
+					} else {
+						ReportManagerImpl.this.multiCaster
+								.notify(new DataflowReportEvent(dataflow));
+					}
+				} else {
+					ReportManagerImpl.this.multiCaster.notify(new DataflowReportEvent(dataflow));
+				}
+			}
+		}
+
+	}
+
+	private MultiCaster<ReportManagerEvent> multiCaster = new MultiCaster<ReportManagerEvent>(this);
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see
+	 * net.sf.taverna.t2.workbench.report.ReportManagerI#addObserver(net.sf.taverna.t2.lang.observer
+	 * .Observer)
+	 */
+	@Override
+	public void addObserver(Observer<ReportManagerEvent> observer) {
+		multiCaster.addObserver(observer);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see net.sf.taverna.t2.workbench.report.ReportManagerI#getObservers()
+	 */
+	@Override
+	public List<Observer<ReportManagerEvent>> getObservers() {
+		return multiCaster.getObservers();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see
+	 * net.sf.taverna.t2.workbench.report.ReportManagerI#removeObserver(net.sf.taverna.t2.lang.observer
+	 * .Observer)
+	 */
+	@Override
+	public void removeObserver(Observer<ReportManagerEvent> observer) {
+		multiCaster.removeObserver(observer);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see net.sf.taverna.t2.workbench.report.ReportManagerI#isReportOutdated(net.sf.taverna.t2.
+	 * workflowmodel.Dataflow)
+	 */
+	@Override
+	public boolean isReportOutdated(Dataflow dataflow) {
+		String lastCheckedId = lastFullCheckedDataflowIdMap.get(dataflow);
+		Long lastCheck = lastFullCheckedMap.get(dataflow);
+		if (lastCheckedId == null || lastCheck == null) {
+			// Unknown, so outdated
+			return true;
+		}
+		if (!lastCheckedId.equals(dataflow.getIdentifier())) {
+			// Workflow changed, so outdaeted
+			return true;
+		}
+		long now = System.currentTimeMillis();
+		long age = now - lastCheck;
+		// Outdated if it is older than the maximum
+		return age > MAX_AGE_OUTDATED_MILLIS;
+	}
+
+	public class ReportManagerEditObserver implements Observer<EditManagerEvent> {
+		public void notify(Observable<EditManagerEvent> sender, EditManagerEvent message)
+				throws Exception {
+			String onEdit = reportManagerConfiguration
+					.getProperty(ReportManagerConfiguration.ON_EDIT);
+			Dataflow dataflow = fileManager.getCurrentDataflow();
+			if (message instanceof AbstractDataflowEditEvent) {
+				AbstractDataflowEditEvent adee = (AbstractDataflowEditEvent) message;
+				if (adee.getDataFlow().equals(dataflow)) {
+					if (onEdit.equals(ReportManagerConfiguration.QUICK_CHECK)) {
+						updateReport(dataflow, false, true);
+					} else if (onEdit.equals(ReportManagerConfiguration.FULL_CHECK)) {
+						updateReport(dataflow, true, true);
+					}
+				}
+			}
+		}
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-report-impl/src/main/resources/META-INF/spring/report-impl-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-workbench-report-impl/src/main/resources/META-INF/spring/report-impl-context-osgi.xml b/taverna-workbench-report-impl/src/main/resources/META-INF/spring/report-impl-context-osgi.xml
new file mode 100644
index 0000000..31c7432
--- /dev/null
+++ b/taverna-workbench-report-impl/src/main/resources/META-INF/spring/report-impl-context-osgi.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:beans="http://www.springframework.org/schema/beans"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans.xsd
+                      http://www.springframework.org/schema/osgi
+                      http://www.springframework.org/schema/osgi/spring-osgi.xsd">
+
+	<!-- <service ref="ProcessorFragilityChecker" interface="net.sf.taverna.t2.visit.fragility.FragilityChecker" /> -->
+
+	<!-- <service ref="FragilityCheck" interface="net.sf.taverna.t2.visit.VisitKind" /> -->
+
+	<service ref="ReportManagerImpl" interface="net.sf.taverna.t2.workbench.report.ReportManager" />
+
+	<service ref="ReportManagerConfiguration" interface="net.sf.taverna.t2.workbench.report.config.ReportManagerConfiguration" />
+
+	<reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" />
+	<reference id="fileManager" interface="net.sf.taverna.t2.workbench.file.FileManager" />
+	<reference id="configurationManager" interface="uk.org.taverna.configuration.ConfigurationManager" />
+
+	<set id="visitors" interface="net.sf.taverna.t2.visit.Visitor" cardinality="0..N" />
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-report-impl/src/main/resources/META-INF/spring/report-impl-context.xml
----------------------------------------------------------------------
diff --git a/taverna-workbench-report-impl/src/main/resources/META-INF/spring/report-impl-context.xml b/taverna-workbench-report-impl/src/main/resources/META-INF/spring/report-impl-context.xml
new file mode 100644
index 0000000..966d50d
--- /dev/null
+++ b/taverna-workbench-report-impl/src/main/resources/META-INF/spring/report-impl-context.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<!-- <bean id="ProcessorFragilityChecker" class="net.sf.taverna.t2.visit.fragility.impl.ProcessorFragilityChecker" /> -->
+
+	<!-- <bean id="FragilityCheck" class="net.sf.taverna.t2.visit.fragility.impl.FragilityCheck" /> -->
+
+	<bean id="ReportManagerImpl" class="net.sf.taverna.t2.workbench.report.impl.ReportManagerImpl">
+    	<constructor-arg ref="editManager" />
+    	<constructor-arg ref="fileManager" />
+    	<constructor-arg ref="visitors" />
+		<constructor-arg ref="ReportManagerConfiguration"/>
+	</bean>
+
+	<bean id="ReportManagerConfiguration" class="net.sf.taverna.t2.workbench.report.config.impl.ReportManagerConfigurationImpl">
+		<constructor-arg ref="configurationManager"/>
+	</bean>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-selection-impl/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-workbench-selection-impl/pom.xml b/taverna-workbench-selection-impl/pom.xml
new file mode 100644
index 0000000..cb6cbcb
--- /dev/null
+++ b/taverna-workbench-selection-impl/pom.xml
@@ -0,0 +1,35 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>net.sf.taverna.t2</groupId>
+		<artifactId>ui-impl</artifactId>
+		<version>2.0-SNAPSHOT</version>
+	</parent>
+	<groupId>net.sf.taverna.t2.ui-impl</groupId>
+	<artifactId>selection-impl</artifactId>
+	<packaging>bundle</packaging>
+	<name>Selection Implementation</name>
+	<dependencies>
+		<dependency>
+			<groupId>net.sf.taverna.t2.ui-api</groupId>
+			<artifactId>edits-api</artifactId>
+			<version>${t2.ui.api.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>net.sf.taverna.t2.ui-api</groupId>
+			<artifactId>file-api</artifactId>
+			<version>${t2.ui.api.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>net.sf.taverna.t2.ui-api</groupId>
+			<artifactId>selection-api</artifactId>
+			<version>${t2.ui.api.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.compendium</artifactId>
+			<version>${osgi.core.version}</version>
+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-selection-impl/src/main/java/net/sf/taverna/t2/workbench/selection/impl/DataflowSelectionModelImpl.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-selection-impl/src/main/java/net/sf/taverna/t2/workbench/selection/impl/DataflowSelectionModelImpl.java b/taverna-workbench-selection-impl/src/main/java/net/sf/taverna/t2/workbench/selection/impl/DataflowSelectionModelImpl.java
new file mode 100644
index 0000000..09d6f71
--- /dev/null
+++ b/taverna-workbench-selection-impl/src/main/java/net/sf/taverna/t2/workbench/selection/impl/DataflowSelectionModelImpl.java
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.selection.impl;
+
+import static net.sf.taverna.t2.workbench.selection.events.DataflowSelectionMessage.Type.ADDED;
+import static net.sf.taverna.t2.workbench.selection.events.DataflowSelectionMessage.Type.REMOVED;
+
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import net.sf.taverna.t2.lang.observer.MultiCaster;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel;
+import net.sf.taverna.t2.workbench.selection.events.DataflowSelectionMessage;
+
+/**
+ * Default implementation of a <code>DataflowSelectionModel</code>.
+ *
+ * @author David Withers
+ */
+public class DataflowSelectionModelImpl implements DataflowSelectionModel {
+	private MultiCaster<DataflowSelectionMessage> multiCaster;
+	private Set<Object> selection = new TreeSet<>(new Comparator<Object>() {
+		@Override
+		public int compare(Object o1, Object o2) {
+			if (o1 == o2)
+				return 0;
+			return o1.hashCode() - o2.hashCode();
+		}
+	});
+
+	/**
+	 * Constructs a new instance of DataflowSelectionModelImpl.
+	 */
+	public DataflowSelectionModelImpl() {
+		multiCaster = new MultiCaster<>(this);
+	}
+
+	@Override
+	public void addSelection(Object element) {
+		if (element != null) {
+			if (!selection.contains(element)) {
+				clearSelection();
+				selection.add(element);
+				multiCaster.notify(new DataflowSelectionMessage(ADDED, element));
+			}
+		}
+	}
+
+	@Override
+	public void clearSelection() {
+		for (Object element : new HashSet<>(selection))
+			removeSelection(element);
+	}
+
+	@Override
+	public Set<Object> getSelection() {
+		return new HashSet<>(selection);
+	}
+
+	@Override
+	public void removeSelection(Object element) {
+		if (element != null && selection.remove(element))
+			multiCaster.notify(new DataflowSelectionMessage(REMOVED, element));
+	}
+
+	@Override
+	public void setSelection(Set<Object> elements) {
+		if (elements == null) {
+			clearSelection();
+			return;
+		}
+		Set<Object> newSelection = new HashSet<>(elements);
+		for (Object element : new HashSet<>(selection))
+			if (!newSelection.remove(element))
+				removeSelection(element);
+		for (Object element : newSelection)
+			addSelection(element);
+	}
+
+	@Override
+	public void addObserver(Observer<DataflowSelectionMessage> observer) {
+		multiCaster.addObserver(observer);
+	}
+
+	@Override
+	public List<Observer<DataflowSelectionMessage>> getObservers() {
+		return multiCaster.getObservers();
+	}
+
+	@Override
+	public void removeObserver(Observer<DataflowSelectionMessage> observer) {
+		multiCaster.removeObserver(observer);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-selection-impl/src/main/java/net/sf/taverna/t2/workbench/selection/impl/SelectionManagerImpl.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-selection-impl/src/main/java/net/sf/taverna/t2/workbench/selection/impl/SelectionManagerImpl.java b/taverna-workbench-selection-impl/src/main/java/net/sf/taverna/t2/workbench/selection/impl/SelectionManagerImpl.java
new file mode 100644
index 0000000..2e86f1a
--- /dev/null
+++ b/taverna-workbench-selection-impl/src/main/java/net/sf/taverna/t2/workbench/selection/impl/SelectionManagerImpl.java
@@ -0,0 +1,367 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.selection.impl;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import net.sf.taverna.t2.lang.observer.MultiCaster;
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.workbench.edits.CompoundEdit;
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.edits.EditManager.DataFlowUndoEvent;
+import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.events.ClosedDataflowEvent;
+import net.sf.taverna.t2.workbench.file.events.FileManagerEvent;
+import net.sf.taverna.t2.workbench.file.events.OpenedDataflowEvent;
+import net.sf.taverna.t2.workbench.file.events.SetCurrentDataflowEvent;
+import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.selection.events.DataflowSelectionMessage;
+import net.sf.taverna.t2.workbench.selection.events.PerspectiveSelectionEvent;
+import net.sf.taverna.t2.workbench.selection.events.ProfileSelectionEvent;
+import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent;
+import net.sf.taverna.t2.workbench.selection.events.WorkflowBundleSelectionEvent;
+import net.sf.taverna.t2.workbench.selection.events.WorkflowRunSelectionEvent;
+import net.sf.taverna.t2.workbench.selection.events.WorkflowSelectionEvent;
+import net.sf.taverna.t2.workbench.ui.zaria.PerspectiveSPI;
+import net.sf.taverna.t2.workflow.edits.AddChildEdit;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+import uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+
+/**
+ * Implementation of the {@link SelectionManager}.
+ *
+ * @author David Withers
+ */
+public class SelectionManagerImpl implements SelectionManager {
+	private static final String RESULTS_PERSPECTIVE_ID = "net.sf.taverna.t2.ui.perspectives.results.ResultsPerspective";
+
+	private static final String DESIGN_PERSPECTIVE_ID = "net.sf.taverna.t2.ui.perspectives.design.DesignPerspective";
+
+	private static final Logger logger = Logger.getLogger(SelectionManagerImpl.class);
+
+	private WorkflowBundle selectedWorkflowBundle;
+	private Map<WorkflowBundle, DataflowSelectionModel> workflowSelectionModels = new IdentityHashMap<>();
+	private Map<WorkflowBundle, Workflow> selectedWorkflows = new IdentityHashMap<>();
+	private Map<WorkflowBundle, Profile> selectedProfiles = new IdentityHashMap<>();
+	private String selectedWorkflowRun;
+	private Map<String, DataflowSelectionModel> workflowRunSelectionModels = new HashMap<>();
+	private PerspectiveSPI selectedPerspective;
+	private MultiCaster<SelectionManagerEvent> observers = new MultiCaster<>(this);
+	private FileManager fileManager;
+	private List<PerspectiveSPI> perspectives;
+
+	@Override
+	public DataflowSelectionModel getDataflowSelectionModel(WorkflowBundle dataflow) {
+		DataflowSelectionModel selectionModel;
+		synchronized (workflowSelectionModels) {
+			selectionModel = workflowSelectionModels.get(dataflow);
+			if (selectionModel == null) {
+				selectionModel = new DataflowSelectionModelImpl();
+				workflowSelectionModels.put(dataflow, selectionModel);
+			}
+		}
+		return selectionModel;
+	}
+
+	@Override
+	public WorkflowBundle getSelectedWorkflowBundle() {
+		return selectedWorkflowBundle;
+	}
+
+	@Override
+	public void setSelectedWorkflowBundle(WorkflowBundle workflowBundle) {
+		setSelectedWorkflowBundle(workflowBundle, true);
+	}
+
+	private void setSelectedWorkflowBundle(WorkflowBundle workflowBundle, boolean notifyFileManager) {
+		if (workflowBundle == null || workflowBundle == selectedWorkflowBundle)
+			return;
+		if (notifyFileManager) {
+			fileManager.setCurrentDataflow(workflowBundle);
+			return;
+		}
+		if (selectedWorkflows.get(workflowBundle) == null)
+			selectedWorkflows.put(workflowBundle,
+					workflowBundle.getMainWorkflow());
+		if (selectedProfiles.get(workflowBundle) == null)
+			selectedProfiles.put(workflowBundle,
+					workflowBundle.getMainProfile());
+		SelectionManagerEvent selectionManagerEvent = new WorkflowBundleSelectionEvent(
+				selectedWorkflowBundle, workflowBundle);
+		selectedWorkflowBundle = workflowBundle;
+		notify(selectionManagerEvent);
+		selectDesignPerspective();
+	}
+
+	private void removeWorkflowBundle(WorkflowBundle dataflow) {
+		synchronized (workflowSelectionModels) {
+			DataflowSelectionModel selectionModel = workflowSelectionModels.remove(dataflow);
+			if (selectionModel != null)
+				for (Observer<DataflowSelectionMessage> observer : selectionModel.getObservers())
+					selectionModel.removeObserver(observer);
+		}
+		synchronized (selectedWorkflows) {
+			selectedWorkflows.remove(dataflow);
+		}
+		synchronized (selectedProfiles) {
+			selectedProfiles.remove(dataflow);
+		}
+	}
+
+	@Override
+	public Workflow getSelectedWorkflow() {
+		return selectedWorkflows.get(selectedWorkflowBundle);
+	}
+
+	@Override
+	public void setSelectedWorkflow(Workflow workflow) {
+		if (workflow != null) {
+			Workflow selectedWorkflow = selectedWorkflows.get(workflow
+					.getParent());
+			if (selectedWorkflow != workflow) {
+				SelectionManagerEvent selectionManagerEvent = new WorkflowSelectionEvent(
+						selectedWorkflow, workflow);
+				selectedWorkflows.put(workflow.getParent(), workflow);
+				notify(selectionManagerEvent);
+			}
+		}
+	}
+
+	@Override
+	public Profile getSelectedProfile() {
+		return selectedProfiles.get(selectedWorkflowBundle);
+	}
+
+	@Override
+	public void setSelectedProfile(Profile profile) {
+		if (profile != null) {
+			Profile selectedProfile = selectedProfiles.get(profile.getParent());
+			if (selectedProfile != profile) {
+				SelectionManagerEvent selectionManagerEvent = new ProfileSelectionEvent(
+						selectedProfile, profile);
+				selectedProfiles.put(profile.getParent(), profile);
+				notify(selectionManagerEvent);
+			}
+		}
+	}
+
+	@Override
+	public String getSelectedWorkflowRun() {
+		return selectedWorkflowRun;
+	}
+
+	@Override
+	public void setSelectedWorkflowRun(String workflowRun) {
+		if ((selectedWorkflowRun == null && workflowRun != null)
+				|| !selectedWorkflowRun.equals(workflowRun)) {
+			SelectionManagerEvent selectionManagerEvent = new WorkflowRunSelectionEvent(
+					selectedWorkflowRun, workflowRun);
+			selectedWorkflowRun = workflowRun;
+			notify(selectionManagerEvent);
+			selectResultsPerspective();
+		}
+	}
+
+	@Override
+	public DataflowSelectionModel getWorkflowRunSelectionModel(String workflowRun) {
+		DataflowSelectionModel selectionModel;
+		synchronized (workflowRunSelectionModels) {
+			selectionModel = workflowRunSelectionModels.get(workflowRun);
+			if (selectionModel == null) {
+				selectionModel = new DataflowSelectionModelImpl();
+				workflowRunSelectionModels.put(workflowRun, selectionModel);
+			}
+		}
+		return selectionModel;
+	}
+
+	@SuppressWarnings("unused")
+	private void removeWorkflowRun(String workflowRun) {
+		synchronized (workflowRunSelectionModels) {
+			DataflowSelectionModel selectionModel = workflowRunSelectionModels
+					.remove(workflowRun);
+			if (selectionModel != null)
+				for (Observer<DataflowSelectionMessage> observer : selectionModel
+						.getObservers())
+					selectionModel.removeObserver(observer);
+		}
+	}
+
+	@Override
+	public PerspectiveSPI getSelectedPerspective() {
+		return selectedPerspective;
+	}
+
+	@Override
+	public void setSelectedPerspective(PerspectiveSPI perspective) {
+		if (selectedPerspective != perspective) {
+			SelectionManagerEvent selectionManagerEvent = new PerspectiveSelectionEvent(
+					selectedPerspective, perspective);
+			selectedPerspective = perspective;
+			notify(selectionManagerEvent);
+		}
+	}
+
+	private void selectDesignPerspective() {
+		for (PerspectiveSPI perspective : perspectives)
+			if (DESIGN_PERSPECTIVE_ID.equals(perspective.getID())) {
+				setSelectedPerspective(perspective);
+				break;
+			}
+	}
+
+	private void selectResultsPerspective() {
+		for (PerspectiveSPI perspective : perspectives)
+			if (RESULTS_PERSPECTIVE_ID.equals(perspective.getID())) {
+				setSelectedPerspective(perspective);
+				break;
+			}
+	}
+
+	@Override
+	public void addObserver(Observer<SelectionManagerEvent> observer) {
+		synchronized (observers) {
+			WorkflowBundle selectedWorkflowBundle = getSelectedWorkflowBundle();
+			Workflow selectedWorkflow = getSelectedWorkflow();
+			Profile selectedProfile = getSelectedProfile();
+			String selectedWorkflowRun = getSelectedWorkflowRun();
+			PerspectiveSPI selectedPerspective = getSelectedPerspective();
+			try {
+				if (selectedWorkflowBundle != null)
+					observer.notify(this, new WorkflowBundleSelectionEvent(
+							null, selectedWorkflowBundle));
+				if (selectedWorkflow != null)
+					observer.notify(this, new WorkflowSelectionEvent(null,
+							selectedWorkflow));
+				if (selectedProfile != null)
+					observer.notify(this, new ProfileSelectionEvent(null,
+							selectedProfile));
+				if (selectedWorkflowRun != null)
+					observer.notify(this, new WorkflowRunSelectionEvent(null,
+							selectedWorkflowRun));
+				if (selectedPerspective != null)
+					observer.notify(this, new PerspectiveSelectionEvent(null,
+							selectedPerspective));
+			} catch (Exception e) {
+				logger.warn("Could not notify " + observer, e);
+			}
+			observers.addObserver(observer);
+		}
+	}
+
+	@Override
+	public void removeObserver(Observer<SelectionManagerEvent> observer) {
+		observers.removeObserver(observer);
+	}
+
+	@Override
+	public List<Observer<SelectionManagerEvent>> getObservers() {
+		return observers.getObservers();
+	}
+
+	public void setFileManager(FileManager fileManager) {
+		this.fileManager = fileManager;
+		setSelectedWorkflowBundle(fileManager.getCurrentDataflow());
+		fileManager.addObserver(new FileManagerObserver());
+	}
+
+	public void setEditManager(EditManager editManager) {
+		editManager.addObserver(new EditManagerObserver());
+	}
+
+	public void setPerspectives(List<PerspectiveSPI> perspectives) {
+		this.perspectives = perspectives;
+	}
+
+	public class FileManagerObserver implements Observer<FileManagerEvent> {
+		@Override
+		public void notify(Observable<FileManagerEvent> sender,
+				FileManagerEvent message) throws Exception {
+			if (message instanceof ClosedDataflowEvent) {
+				WorkflowBundle workflowBundle = ((ClosedDataflowEvent) message).getDataflow();
+				removeWorkflowBundle(workflowBundle);
+			} else if (message instanceof OpenedDataflowEvent) {
+				WorkflowBundle workflowBundle = ((OpenedDataflowEvent) message).getDataflow();
+				setSelectedWorkflowBundle(workflowBundle, false);
+			} else if (message instanceof SetCurrentDataflowEvent) {
+				WorkflowBundle workflowBundle = ((SetCurrentDataflowEvent) message).getDataflow();
+				setSelectedWorkflowBundle(workflowBundle, false);
+			}
+		}
+	}
+
+	private class EditManagerObserver implements Observer<EditManagerEvent> {
+		@Override
+		public void notify(Observable<EditManagerEvent> sender, EditManagerEvent message)
+				throws Exception {
+			Edit<?> edit = message.getEdit();
+			considerEdit(edit, message instanceof DataFlowUndoEvent);
+		}
+
+		private void considerEdit(Edit<?> edit, boolean undoing) {
+			if (edit instanceof CompoundEdit) {
+				CompoundEdit compound = (CompoundEdit) edit;
+				for (Edit<?> e : compound.getChildEdits())
+					considerEdit(e, undoing);
+			} else if (edit instanceof AddChildEdit
+					&& edit.getSubject() instanceof Workflow) {
+				Workflow subject = (Workflow) edit.getSubject();
+				DataflowSelectionModel selectionModel = getDataflowSelectionModel(subject
+						.getParent());
+				Object child = ((AddChildEdit<?>) edit).getChild();
+				if (child instanceof Processor
+						|| child instanceof InputWorkflowPort
+						|| child instanceof OutputWorkflowPort) {
+					if (undoing
+							&& selectionModel.getSelection().contains(child))
+						selectionModel.clearSelection();
+					else {
+						Set<Object> selection = new HashSet<>();
+						selection.add(child);
+						selectionModel.setSelection(selection);
+					}
+				}
+			}
+		}
+	}
+
+	private void notify(SelectionManagerEvent event) {
+		synchronized (observers) {
+			observers.notify(event);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-selection-impl/src/main/resources/META-INF/spring/selection-impl-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-workbench-selection-impl/src/main/resources/META-INF/spring/selection-impl-context-osgi.xml b/taverna-workbench-selection-impl/src/main/resources/META-INF/spring/selection-impl-context-osgi.xml
new file mode 100644
index 0000000..19faa00
--- /dev/null
+++ b/taverna-workbench-selection-impl/src/main/resources/META-INF/spring/selection-impl-context-osgi.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:beans="http://www.springframework.org/schema/beans"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans.xsd
+                      http://www.springframework.org/schema/osgi
+                      http://www.springframework.org/schema/osgi/spring-osgi.xsd">
+
+	<service ref="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" />
+
+	<reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" />
+	<reference id="fileManager" interface="net.sf.taverna.t2.workbench.file.FileManager" />
+	<!-- <reference id="menuManager" interface="net.sf.taverna.t2.ui.menu.MenuManager" /> -->
+
+	<list id="perspectives" interface="net.sf.taverna.t2.workbench.ui.zaria.PerspectiveSPI" cardinality="0..N" />
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-selection-impl/src/main/resources/META-INF/spring/selection-impl-context.xml
----------------------------------------------------------------------
diff --git a/taverna-workbench-selection-impl/src/main/resources/META-INF/spring/selection-impl-context.xml b/taverna-workbench-selection-impl/src/main/resources/META-INF/spring/selection-impl-context.xml
new file mode 100644
index 0000000..84bbff1
--- /dev/null
+++ b/taverna-workbench-selection-impl/src/main/resources/META-INF/spring/selection-impl-context.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<bean id="selectionManager" class="net.sf.taverna.t2.workbench.selection.impl.SelectionManagerImpl" >
+		<property name="fileManager" ref="fileManager"/>
+		<property name="editManager" ref="editManager"/>
+		<property name="perspectives" ref="perspectives"/>
+	</bean>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-update-manager/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-workbench-update-manager/pom.xml b/taverna-workbench-update-manager/pom.xml
new file mode 100644
index 0000000..c2f2003
--- /dev/null
+++ b/taverna-workbench-update-manager/pom.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>net.sf.taverna.t2</groupId>
+		<artifactId>ui-impl</artifactId>
+		<version>2.0-SNAPSHOT</version>
+	</parent>
+	<groupId>net.sf.taverna.t2.ui-impl</groupId>
+	<artifactId>update-manager</artifactId>
+	<version>2.0.1-SNAPSHOT</version>
+	<packaging>bundle</packaging>
+	<name>Taverna Workbench Update Manager</name>
+	<dependencies>
+		<dependency>
+			<groupId>net.sf.taverna.t2.ui-api</groupId>
+			<artifactId>menu-api</artifactId>
+			<version>${t2.ui.api.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>uk.org.taverna.commons</groupId>
+			<artifactId>taverna-update-api</artifactId>
+			<version>${taverna.commons.version}</version>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-update-manager/src/main/java/net/sf/taverna/t2/workbench/update/impl/UpdateManagerView.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-update-manager/src/main/java/net/sf/taverna/t2/workbench/update/impl/UpdateManagerView.java b/taverna-workbench-update-manager/src/main/java/net/sf/taverna/t2/workbench/update/impl/UpdateManagerView.java
new file mode 100644
index 0000000..6b70101
--- /dev/null
+++ b/taverna-workbench-update-manager/src/main/java/net/sf/taverna/t2/workbench/update/impl/UpdateManagerView.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (C) 2013 The University of Manchester
+ *
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.update.impl;
+
+import java.awt.GridBagLayout;
+
+import javax.swing.JPanel;
+
+import uk.org.taverna.commons.update.UpdateManager;
+
+/**
+ * @author David Withers
+ */
+@SuppressWarnings({ "serial", "unused" })
+public class UpdateManagerView extends JPanel {
+
+	private UpdateManager updateManager;
+
+	public UpdateManagerView(UpdateManager updateManager) {
+		this.updateManager = updateManager;
+		initialize();
+	}
+
+	private void initialize() {
+		setLayout(new GridBagLayout());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-update-manager/src/main/java/net/sf/taverna/t2/workbench/update/impl/menu/UpdateMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-update-manager/src/main/java/net/sf/taverna/t2/workbench/update/impl/menu/UpdateMenuAction.java b/taverna-workbench-update-manager/src/main/java/net/sf/taverna/t2/workbench/update/impl/menu/UpdateMenuAction.java
new file mode 100644
index 0000000..2c1459b
--- /dev/null
+++ b/taverna-workbench-update-manager/src/main/java/net/sf/taverna/t2/workbench/update/impl/menu/UpdateMenuAction.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (C) 2013 The University of Manchester
+ *
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.update.impl.menu;
+
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+import static javax.swing.JOptionPane.showMessageDialog;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.net.URI;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.commons.update.UpdateException;
+import uk.org.taverna.commons.update.UpdateManager;
+
+public class UpdateMenuAction extends AbstractMenuAction {
+	private static final Logger logger = Logger.getLogger(UpdateMenuAction.class);
+	private static final URI ADVANCED_MENU_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#advanced");
+
+	private UpdateManager updateManager;
+
+	public UpdateMenuAction() {
+		super(ADVANCED_MENU_URI, 1000);
+	}
+
+	@SuppressWarnings("serial")
+	@Override
+	protected Action createAction() {
+		return new AbstractAction("Check for updates") {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				findUpdates();
+			}
+		};
+	}
+
+	public void setUpdateManager(UpdateManager updateManager) {
+		this.updateManager = updateManager;
+	}
+
+	private void findUpdates() {
+		Component parent = null;
+		try {
+			if (!areUpdatesAvailable()) {
+				showMessageDialog(null, "No update available");
+				return;
+			}
+			if (showConfirmDialog(parent, "Update available. Update Now?") != YES_OPTION)
+				return;
+			applyUpdates();
+			showMessageDialog(parent,
+					"Update complete. Restart Taverna to apply update.");
+		} catch (UpdateException ex) {
+			showMessageDialog(parent, "Update failed: " + ex.getMessage());
+			logger.warn("Update failed", ex);
+		}
+	}
+
+	protected boolean areUpdatesAvailable() throws UpdateException {
+		return updateManager.checkForUpdates();
+	}
+
+	protected void applyUpdates() throws UpdateException {
+		updateManager.update();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-update-manager/src/main/java/net/sf/taverna/t2/workbench/updatemanager/PluginMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-update-manager/src/main/java/net/sf/taverna/t2/workbench/updatemanager/PluginMenuAction.java b/taverna-workbench-update-manager/src/main/java/net/sf/taverna/t2/workbench/updatemanager/PluginMenuAction.java
new file mode 100644
index 0000000..e6b4695
--- /dev/null
+++ b/taverna-workbench-update-manager/src/main/java/net/sf/taverna/t2/workbench/updatemanager/PluginMenuAction.java
@@ -0,0 +1,52 @@
+package net.sf.taverna.t2.workbench.updatemanager;
+
+import static java.awt.event.KeyEvent.VK_F12;
+import static javax.swing.KeyStroke.getKeyStroke;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.net.URI;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+
+public class PluginMenuAction extends AbstractMenuAction {
+	private static final String UPDATES_AND_PLUGINS = "Updates and plugins";
+
+	@SuppressWarnings("serial")
+	protected class SoftwareUpdates extends AbstractAction {
+		public SoftwareUpdates() {
+			super(UPDATES_AND_PLUGINS, null/*UpdatesAvailableIcon.updateRecommendedIcon*/);
+			putValue(ACCELERATOR_KEY, getKeyStroke(VK_F12, 0));
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			@SuppressWarnings("unused")
+			Component parent = null;
+			if (e.getSource() instanceof Component) {
+				parent = (Component) e.getSource();
+			}
+			//FIXME this does nothing!
+			//final PluginManagerFrame pluginManagerUI = new PluginManagerFrame(
+			//		PluginManager.getInstance());
+			//if (parent != null) {
+			//	pluginManagerUI.setLocationRelativeTo(parent);
+			//}
+			//pluginManagerUI.setVisible(true);
+		}
+	}
+
+	public PluginMenuAction() {
+		super(URI.create("http://taverna.sf.net/2008/t2workbench/menu#advanced"),
+				100);
+	}
+
+	@Override
+	protected Action createAction() {
+		//return new SoftwareUpdates();
+		return null;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-update-manager/src/main/java/net/sf/taverna/t2/workbench/updatemanager/UpdatesAvailableMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-update-manager/src/main/java/net/sf/taverna/t2/workbench/updatemanager/UpdatesAvailableMenuAction.java b/taverna-workbench-update-manager/src/main/java/net/sf/taverna/t2/workbench/updatemanager/UpdatesAvailableMenuAction.java
new file mode 100644
index 0000000..e2611be
--- /dev/null
+++ b/taverna-workbench-update-manager/src/main/java/net/sf/taverna/t2/workbench/updatemanager/UpdatesAvailableMenuAction.java
@@ -0,0 +1,19 @@
+package net.sf.taverna.t2.workbench.updatemanager;
+
+import static net.sf.taverna.t2.workbench.updatemanager.UpdatesToolbarSection.UPDATES_SECTION;
+
+import java.awt.Component;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuCustom;
+
+public class UpdatesAvailableMenuAction extends AbstractMenuCustom {
+	public UpdatesAvailableMenuAction() {
+		super(UPDATES_SECTION, 10);
+	}
+
+	@Override
+	protected Component createCustomComponent() {
+		//return new UpdatesAvailableIcon();
+		return null;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-update-manager/src/main/java/net/sf/taverna/t2/workbench/updatemanager/UpdatesToolbarSection.java
----------------------------------------------------------------------
diff --git a/taverna-workbench-update-manager/src/main/java/net/sf/taverna/t2/workbench/updatemanager/UpdatesToolbarSection.java b/taverna-workbench-update-manager/src/main/java/net/sf/taverna/t2/workbench/updatemanager/UpdatesToolbarSection.java
new file mode 100644
index 0000000..b16d614
--- /dev/null
+++ b/taverna-workbench-update-manager/src/main/java/net/sf/taverna/t2/workbench/updatemanager/UpdatesToolbarSection.java
@@ -0,0 +1,16 @@
+package net.sf.taverna.t2.workbench.updatemanager;
+
+import static net.sf.taverna.t2.ui.menu.DefaultToolBar.DEFAULT_TOOL_BAR;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+public class UpdatesToolbarSection extends AbstractMenuSection {
+	public static final URI UPDATES_SECTION = URI
+			.create("http://taverna.sf.net/2008/t2workbench/toolbar#updatesSection");
+
+	public UpdatesToolbarSection() {
+		super(DEFAULT_TOOL_BAR, 10000, UPDATES_SECTION);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/72850d5a/taverna-workbench-update-manager/src/main/resources/META-INF/spring/update-manager-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-workbench-update-manager/src/main/resources/META-INF/spring/update-manager-context-osgi.xml b/taverna-workbench-update-manager/src/main/resources/META-INF/spring/update-manager-context-osgi.xml
new file mode 100644
index 0000000..ea637dd
--- /dev/null
+++ b/taverna-workbench-update-manager/src/main/resources/META-INF/spring/update-manager-context-osgi.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans:beans xmlns="http://www.springframework.org/schema/osgi"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans.xsd
+                      http://www.springframework.org/schema/osgi
+                      http://www.springframework.org/schema/osgi/spring-osgi.xsd">
+
+	<service ref="UpdateMenuAction" auto-export="interfaces" />
+
+	<reference id="updateManager" interface="uk.org.taverna.commons.update.UpdateManager" />
+
+</beans:beans>