Refactor bundles view

git-svn-id: https://svn.apache.org/repos/asf/karaf/sandbox/pieber/karaf-webconsole/trunk@1160483 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/core/src/main/java/org/apache/karaf/webconsole/core/panel/AbstractImagePanel.java b/core/src/main/java/org/apache/karaf/webconsole/core/panel/AbstractImagePanel.java
new file mode 100644
index 0000000..21afbba
--- /dev/null
+++ b/core/src/main/java/org/apache/karaf/webconsole/core/panel/AbstractImagePanel.java
@@ -0,0 +1,13 @@
+package org.apache.karaf.webconsole.core.panel;
+import org.apache.wicket.markup.html.panel.Panel;
+
+/**
+ * Base class and markup for image panels.
+ */
+public class AbstractImagePanel extends Panel {
+
+    public AbstractImagePanel(String id) {
+        super(id);
+    }
+
+}
diff --git a/core/src/main/java/org/apache/karaf/webconsole/core/panel/CssImagePanel.java b/core/src/main/java/org/apache/karaf/webconsole/core/panel/CssImagePanel.java
new file mode 100644
index 0000000..9ca5ad9
--- /dev/null
+++ b/core/src/main/java/org/apache/karaf/webconsole/core/panel/CssImagePanel.java
@@ -0,0 +1,21 @@
+package org.apache.karaf.webconsole.core.panel;
+
+import static org.apache.wicket.model.Model.of;
+
+import org.apache.wicket.behavior.AttributeAppender;
+import org.apache.wicket.markup.html.basic.Label;
+
+/**
+ * Panel which creates an span element with background image.
+ */
+public class CssImagePanel extends AbstractImagePanel {
+
+    public CssImagePanel(String id, String cssClass) {
+        super(id);
+
+        Label label = new Label("decorationLabel");
+        label.add(new AttributeAppender("class", of(cssClass), " "));
+        add(label);
+    }
+
+}
diff --git a/core/src/main/java/org/apache/karaf/webconsole/core/panel/StaticImagePanel.java b/core/src/main/java/org/apache/karaf/webconsole/core/panel/StaticImagePanel.java
new file mode 100644
index 0000000..df321d4
--- /dev/null
+++ b/core/src/main/java/org/apache/karaf/webconsole/core/panel/StaticImagePanel.java
@@ -0,0 +1,17 @@
+package org.apache.karaf.webconsole.core.panel;
+
+import org.apache.wicket.ResourceReference;
+import org.apache.wicket.markup.html.image.Image;
+
+/**
+ * Panel which wraps an image.
+ */
+public class StaticImagePanel extends AbstractImagePanel {
+
+    public StaticImagePanel(String id, ResourceReference resource) {
+        super(id);
+
+        add(new Image("decorationImage", resource));
+    }
+
+}
diff --git a/core/src/main/java/org/apache/karaf/webconsole/core/table/PropertyColumnExt.java b/core/src/main/java/org/apache/karaf/webconsole/core/table/PropertyColumnExt.java
new file mode 100644
index 0000000..0d16fdf
--- /dev/null
+++ b/core/src/main/java/org/apache/karaf/webconsole/core/table/PropertyColumnExt.java
@@ -0,0 +1,16 @@
+package org.apache.karaf.webconsole.core.table;
+
+import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.model.Model;
+
+public class PropertyColumnExt<T> extends PropertyColumn<T> {
+
+    public PropertyColumnExt(String label, String property) {
+        super(Model.of(label), property);
+    }
+
+    public PropertyColumnExt(String property) {
+        this(property, property);
+    }
+
+}
diff --git a/core/src/main/resources/org/apache/karaf/webconsole/core/panel/AbstractImagePanel.html b/core/src/main/resources/org/apache/karaf/webconsole/core/panel/AbstractImagePanel.html
new file mode 100644
index 0000000..25379bc
--- /dev/null
+++ b/core/src/main/resources/org/apache/karaf/webconsole/core/panel/AbstractImagePanel.html
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <title>Karaf wicket console</title>
+</head>
+<body>
+
+    <wicket:panel>
+        <div class="decoration image-decoration">
+            <wicket:child />
+        </div>
+    </wicket:panel>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/core/src/main/resources/org/apache/karaf/webconsole/core/panel/CssImagePanel.html b/core/src/main/resources/org/apache/karaf/webconsole/core/panel/CssImagePanel.html
new file mode 100644
index 0000000..2cd19ce
--- /dev/null
+++ b/core/src/main/resources/org/apache/karaf/webconsole/core/panel/CssImagePanel.html
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <title>Karaf wicket console</title>
+</head>
+<body>
+
+    <wicket:extend>
+        <span wicket:id="decorationLabel"></span>
+    </wicket:extend>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/core/src/main/resources/org/apache/karaf/webconsole/core/panel/StaticImagePanel.html b/core/src/main/resources/org/apache/karaf/webconsole/core/panel/StaticImagePanel.html
new file mode 100644
index 0000000..01f599c
--- /dev/null
+++ b/core/src/main/resources/org/apache/karaf/webconsole/core/panel/StaticImagePanel.html
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <title>Karaf wicket console</title>
+</head>
+<body>
+
+    <wicket:extend>
+        <img wicket:id="decorationImage" />
+    </wicket:extend>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/osgi/pom.xml b/osgi/pom.xml
index c4f599b..79cc99e 100644
--- a/osgi/pom.xml
+++ b/osgi/pom.xml
@@ -45,6 +45,10 @@
                             org.ops4j.pax.wicket.util,
                             org.ops4j.pax.wicket.util.proxy
                         </Import-Package>
+                        <Export-Package>
+                            org.apache.karaf.webconsole.osgi.bundle,
+                            org.apache.karaf.webconsole.osgi.bundle.view
+                        </Export-Package>
                     </instructions>
                 </configuration>
             </plugin>
diff --git a/osgi/src/main/java/org/apache/karaf/webconsole/osgi/bundle/IActionProvider.java b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/bundle/IActionProvider.java
new file mode 100644
index 0000000..bc843fa
--- /dev/null
+++ b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/bundle/IActionProvider.java
@@ -0,0 +1,10 @@
+package org.apache.karaf.webconsole.osgi.bundle;
+
+import org.apache.wicket.Component;
+import org.osgi.framework.Bundle;
+
+public interface IActionProvider {
+
+    Component create(String componentId, Bundle object);
+
+}
diff --git a/osgi/src/main/java/org/apache/karaf/webconsole/osgi/bundle/IColumnProvider.java b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/bundle/IColumnProvider.java
new file mode 100644
index 0000000..e3b3d0e
--- /dev/null
+++ b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/bundle/IColumnProvider.java
@@ -0,0 +1,10 @@
+package org.apache.karaf.webconsole.osgi.bundle;
+
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.osgi.framework.Bundle;
+
+public interface IColumnProvider {
+
+    IColumn<Bundle> getColumn();
+
+}
diff --git a/osgi/src/main/java/org/apache/karaf/webconsole/osgi/bundle/IDecorationProvider.java b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/bundle/IDecorationProvider.java
new file mode 100644
index 0000000..2523c6b
--- /dev/null
+++ b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/bundle/IDecorationProvider.java
@@ -0,0 +1,21 @@
+package org.apache.karaf.webconsole.osgi.bundle;
+
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.IModel;
+import org.osgi.framework.Bundle;
+
+/**
+ * Decoration provider is responsible for adding new images in bundles list view.
+ */
+public interface IDecorationProvider {
+
+    /**
+     * Creates new panel.
+     * 
+     * @param componentId Identifier of panel.
+     * @param model Model
+     * @return Panel.
+     */
+    Panel getDecoration(String componentId, IModel<Bundle> model);
+
+}
diff --git a/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/BundlesDataProvider.java b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/BundlesDataProvider.java
new file mode 100644
index 0000000..5631b6f
--- /dev/null
+++ b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/BundlesDataProvider.java
@@ -0,0 +1,37 @@
+package org.apache.karaf.webconsole.osgi.internal.bundle;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.karaf.webconsole.osgi.internal.bundle.model.BundleModel;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
+import org.apache.wicket.model.IModel;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+public class BundlesDataProvider extends SortableDataProvider<Bundle> {
+
+    private BundleContext context;
+
+    public BundlesDataProvider(BundleContext context) {
+        this.context = context;
+    }
+
+    public Iterator<? extends Bundle> iterator(int first, int count) {
+        List<Bundle> bundles = Arrays.asList(context.getBundles());
+
+        return bundles.subList(first, first + count).iterator();
+    }
+
+    public IModel<Bundle> model(Bundle object) {
+        return new BundleModel(object.getBundleId(), context);
+    }
+
+    public int size() {
+        return context.getBundles().length;
+    }
+
+
+
+}
diff --git a/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/BundlesPage.java b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/BundlesPage.java
index d0af169..8cb44ac 100644
--- a/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/BundlesPage.java
+++ b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/BundlesPage.java
@@ -1,57 +1,59 @@
 package org.apache.karaf.webconsole.osgi.internal.bundle;
 
-import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.karaf.webconsole.core.table.PropertyColumnExt;
+import org.apache.karaf.webconsole.osgi.bundle.IActionProvider;
+import org.apache.karaf.webconsole.osgi.bundle.IColumnProvider;
+import org.apache.karaf.webconsole.osgi.bundle.IDecorationProvider;
 import org.apache.karaf.webconsole.osgi.internal.OsgiPage;
-import org.apache.wicket.PageParameters;
-import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.markup.html.link.BookmarkablePageLink;
-import org.apache.wicket.markup.html.list.ListItem;
-import org.apache.wicket.markup.html.list.ListView;
+import org.apache.karaf.webconsole.osgi.internal.bundle.view.BundlesDataTable;
+import org.apache.karaf.webconsole.osgi.internal.bundle.view.DecorationPanel;
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.ops4j.pax.wicket.api.PaxWicketBean;
 import org.ops4j.pax.wicket.api.PaxWicketMountPoint;
 import org.osgi.framework.Bundle;
 
 @PaxWicketMountPoint(mountPoint = "/osgi/bundles")
 public class BundlesPage extends OsgiPage {
 
+    @PaxWicketBean(name = "columnProviders")
+    private List<IColumnProvider> columnProviders;
+
+    @PaxWicketBean(name = "actionProviders")
+    private List<IActionProvider> actionProviders;
+
+    @PaxWicketBean(name = "decorationProviders")
+    private List<IDecorationProvider> decorationProviders;
+
     public BundlesPage() {
-
-        List<Bundle> model = Arrays.asList(context.getBundles());
-
-        add(new ListView<Bundle>("bundles", model) {
-            @Override
-            protected void populateItem(ListItem<Bundle> item) {
-                final Bundle bundle = item.getModelObject();
-                item.add(new Label("id", "" + bundle.getBundleId()));
-                item.add(new Label("symbolicName", bundle.getSymbolicName()));
-                item.add(new Label("version", bundle.getVersion().toString()));
-
-//                List<String> classes = new ArrayList<String>() {
-//                    @Override
-//                    public String toString() {
-//                        String toString = "";
-//                        for (String item : this) {
-//                            toString += " " + item;
-//                        }
-//                        return toString;
-//                    }
-//                };
-
-//                for (ItemClassModifier modifier : modifiers) {
-//                    classes.addAll(modifier.getCssClasses(bundle));
-//                }
-
-//                item.add(new SimpleAttributeModifier("class", classes.toString()));
-
-
-                PageParameters params = new PageParameters();
-                params.put("bundleId", bundle.getBundleId());
-
-                item.add(new BookmarkablePageLink<DetailsPage>("link", DetailsPage.class, params));
+        List<IColumn<Bundle>> columns = new ArrayList<IColumn<Bundle>>();
+        columns.add(new AbstractColumn<Bundle>(Model.of("")) {
+            public void populateItem(Item<ICellPopulator<Bundle>> cellItem, final String componentId, final IModel<Bundle> rowModel) {
+                cellItem.add(new DecorationPanel(componentId, rowModel, decorationProviders));
             }
         });
+        columns.add(new PropertyColumnExt<Bundle>("Bundle Id", "bundleId"));
 
+        for (IColumnProvider provider : columnProviders) {
+            columns.add(provider.getColumn());
+        }
+
+        columns.add(new PropertyColumnExt<Bundle>("Name", "symbolicName"));
+        columns.add(new PropertyColumnExt<Bundle>("Version", "version.toString"));
+//        columns.add(new AbstractColumn<Bundle>(Model.of("Operations")) {
+//            public void populateItem(Item<ICellPopulator<Bundle>> cellItem, final String componentId, final IModel<Bundle> rowModel) {
+//                cellItem.add(new BundleActionsPanel(componentId, rowModel, actionProviders));
+//            }
+//        });
+
+        add(new BundlesDataTable("bundles", columns, new BundlesDataProvider(context), 100));
     }
 
 }
diff --git a/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/ItemClassModifier.java b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/ItemClassModifier.java
deleted file mode 100644
index 23290aa..0000000
--- a/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/ItemClassModifier.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.apache.karaf.webconsole.osgi.internal.bundle;
-
-import java.util.List;
-
-import org.osgi.framework.Bundle;
-
-public interface ItemClassModifier {
-
-    List<String> getCssClasses(Bundle bundle);
-
-}
\ No newline at end of file
diff --git a/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/decorator/SystemBundleClassModifier.java b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/decorator/SystemBundleClassModifier.java
deleted file mode 100644
index 2399dc1..0000000
--- a/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/decorator/SystemBundleClassModifier.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package org.apache.karaf.webconsole.osgi.internal.bundle.decorator;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.karaf.webconsole.osgi.internal.bundle.ItemClassModifier;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.startlevel.StartLevel;
-
-class SystemBundleClassModifier implements ItemClassModifier {
-
-    public List<String> getCssClasses(Bundle bundle) {
-        BundleContext context = bundle.getBundleContext();
-        List<String> classes = new ArrayList<String>();
-
-        if (bundle.getBundleId() == 0) {
-            classes.add("framework");
-        }
-
-        ServiceReference startLevelReference = context.getServiceReference(StartLevel.class.getName());
-
-        if (startLevelReference != null) {
-            StartLevel startLevel = (StartLevel) context.getService(startLevelReference);
-
-            int bundleStartLevel = startLevel.getBundleStartLevel(bundle);
-            if (bundleStartLevel < startLevel.getInitialBundleStartLevel()) {
-                classes.add("system");
-            }
-            context.ungetService(startLevelReference);
-        }
-
-        return classes;
-    }
-}
\ No newline at end of file
diff --git a/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/model/BundleModel.java b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/model/BundleModel.java
new file mode 100644
index 0000000..ee288e5
--- /dev/null
+++ b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/model/BundleModel.java
@@ -0,0 +1,22 @@
+package org.apache.karaf.webconsole.osgi.internal.bundle.model;
+
+import org.apache.wicket.model.LoadableDetachableModel;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+public class BundleModel extends LoadableDetachableModel<Bundle> {
+
+    private long bundleId;
+    private BundleContext context;
+
+    public BundleModel(long bundleId, BundleContext context) {
+        this.bundleId = bundleId;
+        this.context = context;
+    }
+
+    @Override
+    protected Bundle load() {
+        return context.getBundle(bundleId);
+    }
+
+}
diff --git a/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/model/BundlesModel.java b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/model/BundlesModel.java
new file mode 100644
index 0000000..44f9a60
--- /dev/null
+++ b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/model/BundlesModel.java
@@ -0,0 +1,20 @@
+package org.apache.karaf.webconsole.osgi.internal.bundle.model;
+
+import org.apache.wicket.model.LoadableDetachableModel;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+public class BundlesModel extends LoadableDetachableModel<Bundle[]> {
+
+    private final BundleContext context;
+
+    public BundlesModel(BundleContext context) {
+        this.context = context;
+    }
+
+    @Override
+    protected Bundle[] load() {
+        return context.getBundles();
+    }
+
+}
diff --git a/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/view/BundleActionsPanel.java b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/view/BundleActionsPanel.java
new file mode 100644
index 0000000..7f56ad8
--- /dev/null
+++ b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/view/BundleActionsPanel.java
@@ -0,0 +1,47 @@
+package org.apache.karaf.webconsole.osgi.internal.bundle.view;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.karaf.webconsole.core.table.ActionsPanel;
+import org.apache.karaf.webconsole.osgi.bundle.IActionProvider;
+import org.apache.karaf.webconsole.osgi.internal.bundle.DetailsPage;
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.link.Link;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.util.ListModel;
+import org.osgi.framework.Bundle;
+
+public class BundleActionsPanel extends ActionsPanel<Bundle> {
+
+    public BundleActionsPanel(String componentId, final IModel<Bundle> model, List<IActionProvider> actionProviders) {
+        super(componentId, model);
+
+        add(new ListView<IActionProvider>("extensions", new ListModel<IActionProvider>(actionProviders)) {
+            @Override
+            protected void populateItem(ListItem<IActionProvider> item) {
+                item.add(item.getModelObject().create("extension", model.getObject()));
+            }
+        });
+    }
+
+    @Override
+    protected List<Link> getLinks(Bundle object, String id) {
+        PageParameters params = new PageParameters();
+        params.put("bundleId", object.getBundleId());
+
+        List<Link> links = new ArrayList<Link>();
+
+        // details link
+        Link link = new BookmarkablePageLink<DetailsPage>(id, DetailsPage.class, params);
+        link.add(new Label("label", "Details"));
+
+        links.add(link);
+
+        return links;
+    }
+}
diff --git a/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/view/BundlesDataTable.java b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/view/BundlesDataTable.java
new file mode 100644
index 0000000..e80f0ef
--- /dev/null
+++ b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/view/BundlesDataTable.java
@@ -0,0 +1,16 @@
+package org.apache.karaf.webconsole.osgi.internal.bundle.view;
+
+import java.util.List;
+
+import org.apache.wicket.extensions.markup.html.repeater.data.table.DefaultDataTable;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.ISortableDataProvider;
+import org.osgi.framework.Bundle;
+
+public class BundlesDataTable extends DefaultDataTable<Bundle> {
+
+    public BundlesDataTable(String id, List<IColumn<Bundle>> columns, ISortableDataProvider<Bundle> dataProvider, int rowsPerPage) {
+        super(id, columns, dataProvider, rowsPerPage);
+    }
+
+}
diff --git a/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/view/DecorationPanel.java b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/view/DecorationPanel.java
new file mode 100644
index 0000000..84ca1ed
--- /dev/null
+++ b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/view/DecorationPanel.java
@@ -0,0 +1,39 @@
+package org.apache.karaf.webconsole.osgi.internal.bundle.view;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.karaf.webconsole.osgi.bundle.IDecorationProvider;
+import org.apache.wicket.Component;
+import org.apache.wicket.markup.html.CSSPackageResource;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.IModel;
+import org.osgi.framework.Bundle;
+
+public class DecorationPanel extends Panel {
+
+    public DecorationPanel(String id, IModel<Bundle> model, List<IDecorationProvider> decorationProviders) {
+        super(id, model);
+
+        add(CSSPackageResource.getHeaderContribution(DecorationPanel.class, "decoration.css"));
+
+        List<Component> components = new ArrayList<Component>();
+        for (IDecorationProvider provider : decorationProviders) {
+            Component decoration = provider.getDecoration("extension", model);
+            if (decoration != null) {
+                components.add(decoration);
+            }
+        }
+
+        add(new ListView<Component>("extensions", components) {
+            @Override
+            protected void populateItem(ListItem<Component> item) {
+                item.add(item.getModelObject());
+            }
+        });
+
+    }
+
+}
diff --git a/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/view/SystemBundleDecorationProvider.java b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/view/SystemBundleDecorationProvider.java
new file mode 100644
index 0000000..06ea67c
--- /dev/null
+++ b/osgi/src/main/java/org/apache/karaf/webconsole/osgi/internal/bundle/view/SystemBundleDecorationProvider.java
@@ -0,0 +1,28 @@
+package org.apache.karaf.webconsole.osgi.internal.bundle.view;
+
+import org.apache.karaf.webconsole.core.panel.CssImagePanel;
+import org.apache.karaf.webconsole.osgi.bundle.IDecorationProvider;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.IModel;
+import org.osgi.framework.Bundle;
+import org.osgi.service.startlevel.StartLevel;
+
+public class SystemBundleDecorationProvider implements IDecorationProvider {
+
+    private StartLevel startLevel;
+
+    public SystemBundleDecorationProvider(StartLevel startLevel) {
+        this.startLevel = startLevel;
+    }
+
+    public Panel getDecoration(String componentId, IModel<Bundle> model) {
+        Bundle bundle = model.getObject();
+
+        int bundleStartLevel = startLevel.getBundleStartLevel(bundle);
+        if (bundleStartLevel < startLevel.getInitialBundleStartLevel()) {
+            return new CssImagePanel(componentId, "system");
+        }
+
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/osgi/src/main/resources/OSGI-INF/blueprint/osgi.xml b/osgi/src/main/resources/OSGI-INF/blueprint/osgi.xml
index 7e9d78b..b69a046 100644
--- a/osgi/src/main/resources/OSGI-INF/blueprint/osgi.xml
+++ b/osgi/src/main/resources/OSGI-INF/blueprint/osgi.xml
@@ -32,5 +32,16 @@
     </bean>
 
     <reference id="configurationAdmin" interface="org.osgi.service.cm.ConfigurationAdmin" availability="optional" />
+    <reference id="startLevel" interface="org.osgi.service.startlevel.StartLevel" />
+
+    <reference-list id="columnProviders" interface="org.apache.karaf.webconsole.osgi.bundle.IColumnProvider" availability="optional" />
+    <reference-list id="actionProviders" interface="org.apache.karaf.webconsole.osgi.bundle.IActionProvider" availability="optional" />
+    <reference-list id="decorationProviders" interface="org.apache.karaf.webconsole.osgi.bundle.IDecorationProvider" availability="optional" />
+
+    <service auto-export="interfaces">
+        <bean class="org.apache.karaf.webconsole.osgi.internal.bundle.view.SystemBundleDecorationProvider">
+            <argument ref="startLevel" />
+        </bean>
+    </service>
 
 </blueprint>
diff --git a/osgi/src/main/resources/org/apache/karaf/webconsole/osgi/internal/bundle/BundlesPage.html b/osgi/src/main/resources/org/apache/karaf/webconsole/osgi/internal/bundle/BundlesPage.html
index 06b4ddc..808c2f3 100644
--- a/osgi/src/main/resources/org/apache/karaf/webconsole/osgi/internal/bundle/BundlesPage.html
+++ b/osgi/src/main/resources/org/apache/karaf/webconsole/osgi/internal/bundle/BundlesPage.html
@@ -7,23 +7,9 @@
     <body>
         <wicket:extend>
             <h1>Bundles</h1>
-            <table>
-                <tr>
-                    <th>ID</th>
-                    <th>Symbolic name</th>
-                    <th>Version</th>
-                    <th>Details</th>
-                </tr>
 
-                <tr wicket:id="bundles">
-                    <td wicket:id="id">Id</td>
-                    <td wicket:id="symbolicName">Symbolic name</td>
-                    <td wicket:id="version">Version</td>
-                    <td>
-                        <a wicket:id="link" href="DetailsPage.html">bundle details</a>
-                    </td>
-                </tr>
-            </table>
+            <table wicket:id="bundles" class="dataview" />
+
         </wicket:extend>
     </body>
 </html>
diff --git a/osgi/src/main/resources/org/apache/karaf/webconsole/osgi/internal/bundle/view/DecorationPanel.html b/osgi/src/main/resources/org/apache/karaf/webconsole/osgi/internal/bundle/view/DecorationPanel.html
new file mode 100644
index 0000000..e744fb9
--- /dev/null
+++ b/osgi/src/main/resources/org/apache/karaf/webconsole/osgi/internal/bundle/view/DecorationPanel.html
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <title>Karaf wicket console</title>
+</head>
+<body>
+
+    <wicket:panel>
+        <div class="decorations">
+            <ul>
+                <li wicket:id="extensions">
+                    <div wicket:id="extension" class="decoration" />
+                </li>
+            </ul>
+        </div>
+    </wicket:panel>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/osgi/src/main/resources/org/apache/karaf/webconsole/osgi/internal/bundle/view/decoration.css b/osgi/src/main/resources/org/apache/karaf/webconsole/osgi/internal/bundle/view/decoration.css
new file mode 100644
index 0000000..cd76156
--- /dev/null
+++ b/osgi/src/main/resources/org/apache/karaf/webconsole/osgi/internal/bundle/view/decoration.css
@@ -0,0 +1,23 @@
+div.decorations {
+    display: inline;
+}
+
+div.decorations ul {
+    list-style-type: none;
+    margin: 0px;
+}
+
+div.decorations ul li {
+    display: inline;
+}
+
+div.decoration {
+    display: inline;
+}
+
+span.system {
+    background: url("system.gif");
+    width: 16px;
+    height: 16px;
+    display: block;
+}
diff --git a/osgi/src/main/resources/org/apache/karaf/webconsole/osgi/internal/bundle/view/system.gif b/osgi/src/main/resources/org/apache/karaf/webconsole/osgi/internal/bundle/view/system.gif
new file mode 100644
index 0000000..5b881d8
--- /dev/null
+++ b/osgi/src/main/resources/org/apache/karaf/webconsole/osgi/internal/bundle/view/system.gif
Binary files differ