You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ace.apache.org by ma...@apache.org on 2010/11/18 00:54:58 UTC

svn commit: r1036290 - /incubator/ace/trunk/ace-webui-vaadin/src/main/java/org/apache/ace/webui/vaadin/VaadinClient.java

Author: marrs
Date: Wed Nov 17 23:54:58 2010
New Revision: 1036290

URL: http://svn.apache.org/viewvc?rev=1036290&view=rev
Log:
Still far from perfect, but we now have a simple UI to import bundles from the OBR into ACE. Selections and assocations are still a bit 'buggy' though.

Modified:
    incubator/ace/trunk/ace-webui-vaadin/src/main/java/org/apache/ace/webui/vaadin/VaadinClient.java

Modified: incubator/ace/trunk/ace-webui-vaadin/src/main/java/org/apache/ace/webui/vaadin/VaadinClient.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/ace-webui-vaadin/src/main/java/org/apache/ace/webui/vaadin/VaadinClient.java?rev=1036290&r1=1036289&r2=1036290&view=diff
==============================================================================
--- incubator/ace/trunk/ace-webui-vaadin/src/main/java/org/apache/ace/webui/vaadin/VaadinClient.java (original)
+++ incubator/ace/trunk/ace-webui-vaadin/src/main/java/org/apache/ace/webui/vaadin/VaadinClient.java Wed Nov 17 23:54:58 2010
@@ -18,20 +18,37 @@
  */
 package org.apache.ace.webui.vaadin;
 
+import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
 import java.net.URL;
+import java.net.URLConnection;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import com.vaadin.ui.*;
-import org.apache.ace.client.repository.*;
-import org.apache.ace.client.repository.object.*;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.apache.ace.client.repository.ObjectRepository;
+import org.apache.ace.client.repository.RepositoryAdmin;
+import org.apache.ace.client.repository.RepositoryAdminLoginContext;
+import org.apache.ace.client.repository.RepositoryObject;
+import org.apache.ace.client.repository.SessionFactory;
+import org.apache.ace.client.repository.object.Artifact2GroupAssociation;
+import org.apache.ace.client.repository.object.ArtifactObject;
+import org.apache.ace.client.repository.object.GatewayObject;
+import org.apache.ace.client.repository.object.Group2LicenseAssociation;
+import org.apache.ace.client.repository.object.GroupObject;
+import org.apache.ace.client.repository.object.License2GatewayAssociation;
+import org.apache.ace.client.repository.object.LicenseObject;
 import org.apache.ace.client.repository.repository.Artifact2GroupAssociationRepository;
 import org.apache.ace.client.repository.repository.ArtifactRepository;
-import org.apache.ace.client.repository.repository.GatewayRepository;
 import org.apache.ace.client.repository.repository.Group2LicenseAssociationRepository;
 import org.apache.ace.client.repository.repository.GroupRepository;
 import org.apache.ace.client.repository.repository.License2GatewayAssociationRepository;
@@ -45,6 +62,10 @@ import org.osgi.framework.InvalidSyntaxE
 import org.osgi.service.log.LogService;
 import org.osgi.service.useradmin.User;
 import org.osgi.service.useradmin.UserAdmin;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
 
 import com.vaadin.data.Item;
 import com.vaadin.data.Property.ValueChangeEvent;
@@ -54,15 +75,20 @@ import com.vaadin.event.Transferable;
 import com.vaadin.event.dd.DragAndDropEvent;
 import com.vaadin.event.dd.DropHandler;
 import com.vaadin.event.dd.TargetDetails;
-import com.vaadin.event.dd.acceptcriteria.AcceptAll;
 import com.vaadin.event.dd.acceptcriteria.AcceptCriterion;
 import com.vaadin.event.dd.acceptcriteria.Or;
 import com.vaadin.terminal.Sizeable;
 import com.vaadin.ui.AbstractSelect.AbstractSelectTargetDetails;
 import com.vaadin.ui.AbstractSelect.VerticalLocationIs;
+import com.vaadin.ui.Button;
 import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.GridLayout;
+import com.vaadin.ui.Table;
 import com.vaadin.ui.Table.CellStyleGenerator;
 import com.vaadin.ui.Table.TableTransferable;
+import com.vaadin.ui.TextField;
+import com.vaadin.ui.VerticalLayout;
+import com.vaadin.ui.Window;
 
 /*
 
@@ -99,7 +125,7 @@ public class VaadinClient extends com.va
     private volatile ArtifactRepository m_artifactRepository;
     private volatile GroupRepository m_featureRepository;
     private volatile LicenseRepository m_distributionRepository;
-    private volatile StatefulGatewayRepository m_targetRepository;
+    private volatile StatefulGatewayRepository m_statefulTargetRepository;
     private volatile Artifact2GroupAssociationRepository m_artifact2GroupAssciationRepository;
     private volatile Group2LicenseAssociationRepository m_group2LicenseAssociationRepository;
     private volatile License2GatewayAssociationRepository m_license2GatewayAssociationRepository;
@@ -120,6 +146,7 @@ public class VaadinClient extends com.va
     private Table m_activeTable;
     private Set<?> m_activeSelection;
     public SelectionListener m_activeSelectionListener;
+    private List<OBREntry> m_obrList;
 
     // basic session ID generator
     private static long generateSessionID() {
@@ -134,10 +161,10 @@ public class VaadinClient extends com.va
         addDependency(component, LicenseRepository.class);
         addDependency(component, ArtifactRepository.class);
         addDependency(component, GroupRepository.class);
-        addDependency(component, StatefulGatewayRepository.class);
         addDependency(component, Artifact2GroupAssociationRepository.class);
         addDependency(component, Group2LicenseAssociationRepository.class);
         addDependency(component, License2GatewayAssociationRepository.class);
+        addDependency(component, StatefulGatewayRepository.class);
     }
     
     private void addDependency(Component component, Class service) {
@@ -195,7 +222,14 @@ public class VaadinClient extends com.va
 
         m_artifactsPanel = createArtifactsPanel(main);
         grid.addComponent(m_artifactsPanel, 0, 2);
-        grid.addComponent(new Button("Add artifact..."), 0, 1);
+        Button addArtifactButton = new Button("Add artifact...");
+        addArtifactButton.addListener(new Button.ClickListener() {
+            public void buttonClick(ClickEvent event) {
+                showAddArtifactDialog(main);
+            }
+        });
+
+        grid.addComponent(addArtifactButton, 0, 1);
 
         m_featuresPanel = createFeaturesPanel(main);
         grid.addComponent(m_featuresPanel, 1, 2);
@@ -211,10 +245,10 @@ public class VaadinClient extends com.va
         
         grid.setRowExpandRatio(2, 1.0f);
 
-        m_artifactsPanel.addListener(new SelectionListener(m_artifactsPanel, m_artifactRepository, new Class[] {}, new Class[] { GroupObject.class, LicenseObject.class, StatefulGatewayObject.class }, new Table[] { m_featuresPanel, m_distributionsPanel, m_targetsPanel }));
-        m_featuresPanel.addListener(new SelectionListener(m_featuresPanel, m_featureRepository, new Class[] { ArtifactObject.class }, new Class[] { LicenseObject.class, StatefulGatewayObject.class }, new Table[] { m_artifactsPanel, m_distributionsPanel, m_targetsPanel }));
-        m_distributionsPanel.addListener(new SelectionListener(m_distributionsPanel, m_distributionRepository, new Class[] { GroupObject.class, ArtifactObject.class }, new Class[] { StatefulGatewayObject.class }, new Table[] { m_artifactsPanel, m_featuresPanel, m_targetsPanel }));
-        m_targetsPanel.addListener(new SelectionListener(m_targetsPanel, m_targetRepository, new Class[] { LicenseObject.class, GroupObject.class, ArtifactObject.class}, new Class[] {}, new Table[] { m_artifactsPanel, m_featuresPanel, m_distributionsPanel }));
+        m_artifactsPanel.addListener(new SelectionListener(m_artifactsPanel, m_artifactRepository, new Class[] {}, new Class[] { GroupObject.class, LicenseObject.class, GatewayObject.class }, new Table[] { m_featuresPanel, m_distributionsPanel, m_targetsPanel }));
+        m_featuresPanel.addListener(new SelectionListener(m_featuresPanel, m_featureRepository, new Class[] { ArtifactObject.class }, new Class[] { LicenseObject.class, GatewayObject.class }, new Table[] { m_artifactsPanel, m_distributionsPanel, m_targetsPanel }));
+        m_distributionsPanel.addListener(new SelectionListener(m_distributionsPanel, m_distributionRepository, new Class[] { GroupObject.class, ArtifactObject.class }, new Class[] { GatewayObject.class }, new Table[] { m_artifactsPanel, m_featuresPanel, m_targetsPanel }));
+        m_targetsPanel.addListener(new SelectionListener(m_targetsPanel, m_statefulTargetRepository, new Class[] { LicenseObject.class, GroupObject.class, ArtifactObject.class}, new Class[] {}, new Table[] { m_artifactsPanel, m_featuresPanel, m_distributionsPanel }));
 
         m_artifactsPanel.setDropHandler(new AssociationDropHandler((Table) null, m_featuresPanel) {
             @Override
@@ -245,13 +279,27 @@ public class VaadinClient extends com.va
 
             @Override
             protected void associateFromRight(String left, String right) {
-                m_license2GatewayAssociationRepository.create(getDistribution(left), getTarget(right).getGatewayObject());
+                StatefulGatewayObject target = getTarget(right);
+                System.out.println("Target is " + target + " and is " + (target.isRegistered() ? "registered." : "not registered yet."));
+                if (!target.isRegistered()) {
+                    target.register();
+                    target.setAutoApprove(true);
+                }
+                m_license2GatewayAssociationRepository.create(getDistribution(left), target.getGatewayObject());
+                System.out.println("Associated!");
             }
         });
         m_targetsPanel.setDropHandler(new AssociationDropHandler(m_distributionsPanel, (Table) null) {
             @Override
             protected void associateFromLeft(String left, String right) {
-                m_license2GatewayAssociationRepository.create(getDistribution(left), getTarget(right).getGatewayObject());
+                StatefulGatewayObject target = getTarget(right);
+                System.out.println("Target is " + target + " and is " + (target.isRegistered() ? "registered." : "not registered yet."));
+                if (!target.isRegistered()) {
+                    target.register();
+                    target.setAutoApprove(true);
+                }
+                m_license2GatewayAssociationRepository.create(getDistribution(left), target.getGatewayObject());
+                System.out.println("Associated!");
             }
 
             @Override
@@ -607,14 +655,17 @@ public class VaadinClient extends com.va
     }
     
     private StatefulGatewayObject getTarget(String name) {
+        System.out.println("getTarget(" + name + ")");
         try {
-            List<StatefulGatewayObject> list = m_targetRepository.get(m_context.createFilter("(" + StatefulGatewayObject.KEY_ID + "=" + name + ")"));
+            List<StatefulGatewayObject> list = m_statefulTargetRepository.get(m_context.createFilter("(" + StatefulGatewayObject.KEY_ID + "=" + name + ")"));
             if (list.size() == 1) {
+                System.out.println("getTarget returning " + list.get(0));
                 return list.get(0);
             }
         }
         catch (InvalidSyntaxException e) {
         }
+        System.out.println("getTarget returning NULL!!!!");
         return null;
     }
 
@@ -638,6 +689,7 @@ public class VaadinClient extends com.va
      * Helper method to find all related {@link RepositoryObject}s in a given 'direction'
      */
     private <FROM extends RepositoryObject, TO extends RepositoryObject> List<TO> getRelated(FROM from, Class<TO> toClass) {
+        // if the SGO is not backed by a GO yet, this will cause an exception
         return from.getAssociations(toClass);
     }
     
@@ -735,7 +787,8 @@ public class VaadinClient extends com.va
             }; // add this to the others
             licenseItem.getItemProperty("button").setValue(removeLinkButton);
         }
-        m_targets = m_targetRepository.get();
+        m_statefulTargetRepository.refresh();
+        m_targets = m_statefulTargetRepository.get();
         m_targetsPanel.removeAllItems();
         for (StatefulGatewayObject target : m_targets) {
             Item targetItem = m_targetsPanel.addItem(target.getID());
@@ -758,6 +811,30 @@ public class VaadinClient extends com.va
             }; // add this to the others
             targetItem.getItemProperty("button").setValue(removeLinkButton);
         }
+        
+//        m_targets = m_targetRepository.get();
+//        m_targetsPanel.removeAllItems();
+//        for (GatewayObject license : m_targets) {
+//            Item targetItem = m_targetsPanel.addItem(license.getID());
+//            targetItem.getItemProperty(OBJECT_NAME).setValue(license.getID());
+//            targetItem.getItemProperty(OBJECT_DESCRIPTION).setValue("?");
+//            Button removeLinkButton = new RemoveLinkButton<GatewayObject>(license, m_distributionsPanel, null) {
+//                @Override
+//                protected void removeLinkFromLeft(GatewayObject object, RepositoryObject other) {
+//                    List<License2GatewayAssociation> associations = object.getAssociationsWith((LicenseObject) other);
+//                    for (License2GatewayAssociation association : associations) {
+//                        System.out.println("> " + association.getLeft() + " <-> " + association.getRight());
+//                        m_license2GatewayAssociationRepository.remove(association);
+//                    }
+//                    m_associatedItems.remove(object);
+//                }
+//
+//                @Override
+//                protected void removeLinkFromRight(GatewayObject object, RepositoryObject other) {
+//                }
+//            }; // add this to the others
+//            targetItem.getItemProperty("button").setValue(removeLinkButton);
+//        }
     }
 
     private abstract class RemoveLinkButton<REPO_OBJECT extends RepositoryObject> extends Button {
@@ -819,6 +896,15 @@ public class VaadinClient extends com.va
         }
         
         public void valueChange(ValueChangeEvent event) {
+            
+            if (m_activeSelection != null && m_activeTable != null) {
+                for (Object val : m_activeSelection) {
+                    m_activeTable.unselect(val);
+                }
+            }
+            
+            
+            
             m_activeSelectionListener = this;
             
             // set the active table
@@ -836,6 +922,18 @@ public class VaadinClient extends com.va
             // remember the active selection too
             m_activeSelection = value;
 
+            
+            
+            
+//            for (Table t : m_tablesToRefresh) {
+//                System.out.println("resetting selected items on other tables " + t);
+//                t.setValue(null);
+//            }
+
+            
+            
+            
+            
             if (value == null) {
                 System.out.println("no selection");
             }
@@ -847,50 +945,70 @@ public class VaadinClient extends com.va
                 for (Object val : value) {
                     System.out.println(" - " + m_table.getItem(val).getItemProperty(OBJECT_NAME) + " " + val);
                     RepositoryObject lo = lookup(val);
-                    
-                    List related = null;
-                    for (int i = 0; i < m_left.length; i++) {
-                        if (i == 0) {
-                            related = getRelated(lo, m_left[i]);
-                            System.out.println("left associated:");
-                            for (Object o : related) {
-                                System.out.println(" -> " + o);
+                    System.out.println("lookup(" + val + ") returned " + lo);
+                    if (lo != null) {
+                        List related = null;
+                        for (int i = 0; i < m_left.length; i++) {
+                            if (i == 0) {
+                                related = getRelated(lo, m_left[i]);
+                                System.out.println("left associated:");
+                                for (Object o : related) {
+                                    System.out.println(" -> " + o);
+                                }
+                                m_associatedItems.addAll(related);
                             }
-                            m_associatedItems.addAll(related);
-                        }
-                        else {
-                            related = getRelated(related, m_left[i]);
-                            System.out.println("left related:");
-                            for (Object o : related) {
-                                System.out.println(" -> " + o);
+                            else {
+                                related = getRelated(related, m_left[i]);
+                                System.out.println("left related:");
+                                for (Object o : related) {
+                                    System.out.println(" -> " + o);
+                                }
+                                m_relatedItems.addAll(related);
                             }
-                            m_relatedItems.addAll(related);
                         }
-                    }
-                    for (int i = 0; i < m_right.length; i++) {
-                        if (i == 0) {
-                            related = getRelated(lo, m_right[i]);
-                            System.out.println("right associated:");
-                            for (Object o : related) {
-                                System.out.println(" -> " + o);
+                        for (int i = 0; i < m_right.length; i++) {
+                            if (i == 0) {
+                                related = getRelated(lo, m_right[i]);
+                                System.out.println("right associated:");
+                                for (Object o : related) {
+                                    System.out.println(" -> " + o);
+                                }
+                                m_associatedItems.addAll(related);
                             }
-                            m_associatedItems.addAll(related);
-                        }
-                        else {
-                            related = getRelated(related, m_right[i]);
-                            System.out.println("right related:");
-                            for (Object o : related) {
-                                System.out.println(" -> " + o);
+                            else {
+                                related = getRelated(related, m_right[i]);
+                                System.out.println("right related:");
+                                for (Object o : related) {
+                                    System.out.println(" -> " + o);
+                                }
+                                m_relatedItems.addAll(related);
                             }
-                            m_relatedItems.addAll(related);
                         }
                     }
+                    System.out.println("summarizing associated:");
+                    for (RepositoryObject ro : m_associatedItems) {
+                        System.out.println("** " + ro);
+                    }
+                    System.out.println("summarizing related:");
+                    for (RepositoryObject ro : m_relatedItems) {
+                        System.out.println("** " + ro);
+                    }
                     
                     for (Table t : m_tablesToRefresh) {
                         System.out.println("refreshing " + t);
-                        t.setValue(null);
+//                        t.setValue(null);
                         t.requestRepaint();
                     }
+                    
+                    System.out.println("summarizing associated:");
+                    for (RepositoryObject ro : m_associatedItems) {
+                        System.out.println("** " + ro);
+                    }
+                    System.out.println("summarizing related:");
+                    for (RepositoryObject ro : m_relatedItems) {
+                        System.out.println("** " + ro);
+                    }
+                    
                     // when switching columns, we need to repaint, but it messes up the
                     // cursor position
 //                    m_table.requestRepaint();
@@ -900,9 +1018,17 @@ public class VaadinClient extends com.va
 
         public RepositoryObject lookup(Object value) {
             for (RepositoryObject object : m_repository.get()) {
-                if (getNamedObject(object).getName().equals(value)) {
-                    System.out.println("Found: " + getNamedObject(object).getName());
-                    return object;
+                System.out.println("..." + object);
+                if (object instanceof StatefulGatewayObject) {
+                    object = ((StatefulGatewayObject) object).getGatewayObject();
+                }
+                NamedObject namedObject = getNamedObject(object);
+                System.out.println("..." + namedObject);
+                if (namedObject != null) {
+                    if (namedObject.getName().equals(value)) {
+                        System.out.println("Found: " + namedObject.getName());
+                        return object;
+                    }
                 }
             }
             return null;
@@ -1068,4 +1194,207 @@ public class VaadinClient extends com.va
             throw new IllegalArgumentException();
         }
     }
+    
+    private void showAddArtifactDialog(final Window main) {
+        final Window featureWindow = new Window();
+        featureWindow.setModal(true);
+        featureWindow.setCaption("Add artifact");
+        featureWindow.setWidth("50em");
+
+        // Configure the windws layout; by default a VerticalLayout
+        VerticalLayout layout = (VerticalLayout) featureWindow.getContent();
+        layout.setMargin(true);
+        layout.setSpacing(true);
+
+        final TextField search = new TextField("search");
+        final Table artifacts = new ArtifactTable(main);
+
+        search.setValue("");
+        try {
+            getBundles(artifacts);
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        layout.addComponent(search);
+        layout.addComponent(artifacts);
+
+        Button close = new Button("Add", new Button.ClickListener() {
+            // inline click-listener
+            public void buttonClick(ClickEvent event) {
+                // close the window by removing it from the parent window
+                (featureWindow.getParent()).removeWindow(featureWindow);
+                // TODO add the selected artifacts
+                for (Object id : artifacts.getItemIds()) {
+                    if (artifacts.isSelected(id)) {
+                        for (OBREntry e : m_obrList) {
+                            if (e.getUri().equals(id)) {
+                                System.out.println("Importing " + e);
+                                try {
+                                    importBundle(e);
+                                }
+                                catch (Exception e1) {
+                                    // TODO Auto-generated catch block
+                                    e1.printStackTrace();
+                                }
+                            }
+                        }
+                    }
+                }
+                updateTableData();
+            }
+        });
+        // The components added to the window are actually added to the window's
+        // layout; you can use either. Alignments are set using the layout
+        layout.addComponent(close);
+        layout.setComponentAlignment(close, "right");
+
+        if (featureWindow.getParent() != null) {
+            // window is already showing
+            main.getWindow().showNotification("Window is already open");
+        } else {
+            // Open the subwindow by adding it to the parent
+            // window
+            main.getWindow().addWindow(featureWindow);
+        }
+        search.focus();
+    }
+    
+    public static class ArtifactTable extends Table {
+        public ArtifactTable(final Window main) {
+            super("Artifacts");
+            addContainerProperty("symbolic name", String.class, null);
+            addContainerProperty("version", String.class, null);
+            setSizeFull();
+            setSelectable(true);
+            setMultiSelect(true);
+            setImmediate(true);
+        }
+    }
+    
+    public void getBundles(Table table) throws Exception {
+        getBundles(table, "http://localhost:8080/obr/");
+    }
+    
+    public void getBundles(Table table, String obrBaseUrl) throws Exception {
+        URL obrBase = new URL(obrBaseUrl);
+        
+        // retrieve the repository.xml as a stream
+        URL url = null;
+        try {
+            url = new URL(obrBase, "repository.xml");
+        }
+        catch (MalformedURLException e) {
+            System.err.println("Error retrieving repository.xml from " + obrBase);
+            throw e;
+        }
+
+        InputStream input = null;
+        NodeList resources = null;
+        try {
+            URLConnection connection = url.openConnection();
+            connection.setUseCaches(false); //We always want the newest repository.xml file.
+            input = connection.getInputStream();
+
+            try {
+                XPath xpath = XPathFactory.newInstance().newXPath();
+                // this XPath expressing will find all 'resource' elements which have an attribute 'uri'.
+                resources = (NodeList) xpath.evaluate("/repository/resource[@uri]", new InputSource(input), XPathConstants.NODESET);
+            }
+            catch (XPathExpressionException e) {
+                System.err.println("Error evaluating XPath expression.");
+                e.printStackTrace(System.err);
+                throw e;
+            }
+        }
+        catch (IOException e) {
+            e.printStackTrace(System.err);
+            throw e;
+        }
+        finally {
+            if (input != null) {
+                try {
+                    input.close();
+                }
+                catch (IOException e) {
+                    // too bad, no worries.
+                }
+            }
+        }
+
+        m_obrList = new ArrayList<OBREntry>();
+        for (int nResource = 0; nResource < resources.getLength(); nResource++) {
+            Node resource = resources.item(nResource);
+            NamedNodeMap attr = resource.getAttributes();
+            String uri = attr.getNamedItem("uri").getTextContent();
+            String symbolicname = attr.getNamedItem("symbolicname").getTextContent();
+            String version = attr.getNamedItem("version").getTextContent();
+            m_obrList.add(new OBREntry(symbolicname, version, uri));
+        }
+
+        // Create a list of filenames from the ArtifactRepository
+        List<OBREntry> fromRepository = new ArrayList<OBREntry>();
+        List<ArtifactObject> artifactObjects = m_artifactRepository.get();
+        artifactObjects.addAll(m_artifactRepository.getResourceProcessors());
+        for (ArtifactObject ao : artifactObjects) {
+            String artifactURL = ao.getURL();
+            if (artifactURL.startsWith(obrBase.toExternalForm())) {
+                // we now know this artifact comes from the OBR we are querying, so we are interested.
+                fromRepository.add(new OBREntry(ao.getName(), null /* TODO version */, new File(artifactURL).getName()));
+            }
+        }
+
+        // remove all urls we already know
+        m_obrList.removeAll(fromRepository);
+        if (m_obrList.isEmpty()) {
+            System.err.println("No data in obr...");
+            return;
+        }
+
+        // Create a list of all bundle names
+        for (OBREntry s : m_obrList) {
+            Item item = table.addItem(s.getUri());
+            item.getItemProperty("symbolic name").setValue(s.getSymbolicName());
+            item.getItemProperty("version").setValue(s.getVersion());
+        }
+    }
+
+    public void importBundle(OBREntry bundle) throws Exception {
+        m_artifactRepository.importArtifact(new URL(new URL(obr), bundle.getUri()), false);
+    }
+    
+    public static class OBREntry {
+        private final String m_symbolicName;
+        private final String m_version;
+        private final String m_uri;
+        
+        public OBREntry(String symbolicName, String version, String uri) {
+            m_symbolicName = symbolicName;
+            m_version = version;
+            m_uri = uri;
+        }
+        
+        public String getVersion() {
+            return m_version;
+        }
+        
+        public String getSymbolicName() {
+            return m_symbolicName;
+        }
+        
+        public String getUri() {
+            return m_uri;
+        }
+
+        @Override
+        public int hashCode() {
+            return m_uri.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return m_uri.equals(((OBREntry) obj).m_uri);
+        }
+    }
 }
\ No newline at end of file