/*
* This class is part of the OSGiJMX plugin for the VisualVM, allowing
* to have some basic administration functionality over an OSGi platform
*
* Copyright (C) 2009 Kiev Gama (kiev.gama at gmail.com)
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
**/
package osgijmx.vvm;
import com.sun.tools.visualvm.application.Application;
import com.sun.tools.visualvm.core.ui.DataSourceView;
import com.sun.tools.visualvm.core.ui.components.DataViewComponent;
import com.sun.tools.visualvm.tools.jmx.JmxModelFactory;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Map.Entry;
import java.util.Set;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.Notification;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.management.JMX;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.CompositeType;
import javax.management.openmbean.TabularData;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableColumnModel;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import org.netbeans.lib.profiler.ui.UIConstants;
import org.netbeans.lib.profiler.ui.UIUtils;
import org.netbeans.lib.profiler.ui.components.JExtendedTable;
import org.netbeans.lib.profiler.ui.components.table.LabelTableCellRenderer;
import org.openide.util.Utilities;
import osgijmx.BundleMXBean;
import osgijmx.OSGiPlatformMXBean;
/**
*
* @author kiev
*/
public class BundlesView extends DataSourceView {
private DataViewComponent dvc;
//Reusing an image from the sources:
private static final String IMAGE_PATH = "/resources/icon.png"; // NOI18N
private JLabel generalInfo;
private JTable bundleTable;
private BundlesTableModel tableModel;
private MBeanServerConnection mbs;
private JTextField bundleURL;
private JButton installBtn;
public BundlesView(Application application) {
super(application, "OSGi", new ImageIcon(Utilities.loadImage(IMAGE_PATH, true)).getImage(), 60, false);
mbs = JmxModelFactory.getJmxModelFor(application).getMBeanServerConnection();
try {
mbs.addNotificationListener(new ObjectName("osgijmx:type=framework"), new NotificationListener() {
public void handleNotification(Notification notification, Object handback) {
updateFrameworkInfo();
tableModel.fireTableDataChanged();
}
}, null, null);
} catch (Exception ex) {
ex.printStackTrace();
}
}
@Override
protected DataViewComponent createComponent() {
initView();
updateFrameworkInfo();
return dvc;
}
private OSGiPlatformMXBean getFrameworkMXBean() throws NullPointerException, MalformedObjectNameException {
ObjectName objName = new ObjectName("osgijmx:type=framework");
OSGiPlatformMXBean osgiProxy = JMX.newMXBeanProxy(mbs, objName, OSGiPlatformMXBean.class);
return osgiProxy;
}
private void updateFrameworkInfo() {
try {
OSGiPlatformMXBean osgiProxy = getFrameworkMXBean();
StringBuilder sb = new StringBuilder();
sb.append("Total Bundles: ");
sb.append(osgiProxy.getBundles().size());
sb.append("
Framework version: ");
sb.append(osgiProxy.getFrameworkVersion());
sb.append("
Framework vendor: ");
sb.append(osgiProxy.getFrameworkVendor());
this.generalInfo.setText(sb.toString());
this.tableModel.setBundles(osgiProxy.getBundles());
osgiProxy.getBundle(1).getBundleHeaders().size();
} catch (MalformedObjectNameException ex) {
this.generalInfo.setText("OSGi MBean not found");
}
}
private void initView() {
generalInfo = new JLabel();
generalInfo.setBorder(BorderFactory.createEmptyBorder(14, 8, 14, 8));
tableModel = new BundlesTableModel(new ArrayList());
bundleTable = new JExtendedTable(tableModel);
bundleTable.setBorder(BorderFactory.createEmptyBorder());
bundleTable.setRowSelectionAllowed(true);
bundleTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
bundleTable.setGridColor(UIConstants.TABLE_VERTICAL_GRID_COLOR);
bundleTable.setSelectionBackground(UIConstants.TABLE_SELECTION_BACKGROUND_COLOR);
bundleTable.setSelectionForeground(UIConstants.TABLE_SELECTION_FOREGROUND_COLOR);
bundleTable.setShowHorizontalLines(UIConstants.SHOW_TABLE_HORIZONTAL_GRID);
bundleTable.setShowVerticalLines(UIConstants.SHOW_TABLE_VERTICAL_GRID);
bundleTable.setRowMargin(UIConstants.TABLE_ROW_MARGIN);
bundleTable.setRowHeight(UIUtils.getDefaultRowHeight() + 2);
final JLabel manifestLabel = new JLabel();
final JLabel entriesLabel = new JLabel();
final JTree tree = new JTree(new DefaultMutableTreeNode("Services"));
final JPopupMenu menu = new JPopupMenu();
menu.add(new BundleAction(BundleActionType.Start));
menu.add(new BundleAction(BundleActionType.Stop));
menu.add(new BundleAction(BundleActionType.Update));
menu.add(new BundleAction(BundleActionType.Uninstall));
LabelTableCellRenderer trailRenderer = new LabelTableCellRenderer(JLabel.TRAILING);
LabelTableCellRenderer leadingRenderer = new LabelTableCellRenderer(JLabel.LEADING);
TableColumnModel colModel = bundleTable.getColumnModel();
for (int i = 0; i < colModel.getColumnCount(); i++) {
if (i == BundlesTableModel.COLUMN_ID) {
colModel.getColumn(i).setCellRenderer(trailRenderer);
colModel.getColumn(i).setPreferredWidth(30);
} else {
colModel.getColumn(i).setCellRenderer(leadingRenderer);
}
}
DataViewComponent.MasterView masterView = new DataViewComponent.MasterView("Bundles Overview", null, generalInfo);
DataViewComponent.MasterViewConfiguration masterConfiguration =
new DataViewComponent.MasterViewConfiguration(false);
//master view and configuration view
dvc = new DataViewComponent(masterView, masterConfiguration);
//Add configuration details to the component, which are the show/hide checkboxes at the top:
dvc.configureDetailsArea(new DataViewComponent.DetailsAreaConfiguration(
"Bundle List", true), DataViewComponent.TOP_LEFT);
dvc.configureDetailsArea(new DataViewComponent.DetailsAreaConfiguration(
"Bundle Details", true), DataViewComponent.BOTTOM_LEFT);
JPanel bundlesPanel = new JPanel(new BorderLayout());
JPanel installBundlePanel = new JPanel();
bundleURL = new JTextField(40);
installBtn = new JButton(new BundleAction(BundleActionType.Install));
installBundlePanel.add(new JLabel("Bundle path: "));
installBundlePanel.add(bundleURL);
installBundlePanel.add(installBtn);
installBundlePanel.setBackground(Color.WHITE);
JScrollPane scroll = new JScrollPane(bundleTable);
scroll.setBorder(BorderFactory.createEmptyBorder());
bundlesPanel.add(scroll, BorderLayout.CENTER);
bundlesPanel.add(installBundlePanel, BorderLayout.SOUTH);
//component detail views
dvc.addDetailsView(new DataViewComponent.DetailsView(
"Bundle List", null, 10, bundlesPanel, null), DataViewComponent.TOP_LEFT);
dvc.addDetailsView(new DataViewComponent.DetailsView(
"Manifest Headers", null, 10, wrapComponent(manifestLabel, BorderLayout.NORTH), null), DataViewComponent.BOTTOM_LEFT);
dvc.addDetailsView(new DataViewComponent.DetailsView(
"Provided Services", null, 10, wrapComponent(tree, BorderLayout.CENTER), null), DataViewComponent.BOTTOM_LEFT);
dvc.addDetailsView(new DataViewComponent.DetailsView(
"Contents", null, 10, wrapComponent(entriesLabel, BorderLayout.NORTH), null), DataViewComponent.BOTTOM_LEFT);
dvc.setOpaque(false);
bundleTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
//updates the detailed data if the bundle selection changes
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting() && !bundleTable.getSelectionModel().isSelectionEmpty()) {
StringBuilder sb = new StringBuilder("");
BundleMXBean bean = getSelectedBundle();
//manifest entries
for (Entry entry : bean.getBundleHeaders().entrySet()) {
sb.append("").append(entry.getKey()).append(": ");
sb.append(entry.getValue()).append("
");
}
manifestLabel.setText(sb.toString());
//files and folder entries
sb = new StringBuilder("");
for (String s : bean.getAllEntries()) {
sb.append(s).append("
");
}
entriesLabel.setText(sb.toString());
//Provided services
try {
//work around...
Object o = mbs.getAttribute(new ObjectName("osgijmx:type=bundle,id=" + bean.getBundleId()), "RegisteredServices");
sb.append(((TabularData) o).values().toArray());
DefaultMutableTreeNode root = new DefaultMutableTreeNode("Services");
parseTreeModel((TabularData) o, root);
tree.setModel(new DefaultTreeModel(root));
} catch (Exception ex) {
//TODO handle errors with user message
ex.printStackTrace();
}
}
}
});
// Set the component to show the popup menu
bundleTable.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent evt) {
showPopup(evt);
}
public void mouseReleased(MouseEvent evt) {
showPopup(evt);
}
private void showPopup(MouseEvent evt) {
if (evt.isPopupTrigger()) {
int rowNumber = bundleTable.rowAtPoint(evt.getPoint());
// Get the ListSelectionModel of the JTable
ListSelectionModel model = bundleTable.getSelectionModel();
if (rowNumber > - 1) {
model.setSelectionInterval(rowNumber, rowNumber);
menu.show(evt.getComponent(), evt.getX(), evt.getY());
}
}
}
});
}
//builds the tree model
private void parseTreeModel(TabularData o, DefaultMutableTreeNode node) {
if (o != null) {
Object[] data = o.values().toArray();
for (Object element : data) {
CompositeData item = (CompositeData) element;
CompositeType type = item.getCompositeType();
Set keys = type.keySet();
ArrayList a = new ArrayList(keys);
for (int i = 0; i < a.size();) {
String key = (String) item.get((String) a.get(i++));
Object val = item.get((String) a.get(i++));
DefaultMutableTreeNode newNode;
if (val instanceof TabularData) {
newNode = new DefaultMutableTreeNode(key);
parseTreeModel((TabularData) val, newNode);
} else {
newNode = new DefaultMutableTreeNode(key + " " + val);
}
node.add(newNode);
}
}
}
}
private JScrollPane wrapComponent(JComponent component, String borderLayoutPos) {
JPanel panel = new JPanel(new BorderLayout());
JScrollPane scroll = new JScrollPane(panel);
panel.setBackground(Color.WHITE);
panel.add(component, borderLayoutPos);
component.setBorder(BorderFactory.createEmptyBorder());
scroll.setBorder(BorderFactory.createEmptyBorder());
scroll.setBackground(Color.WHITE);
return scroll;
}
private BundleMXBean getSelectedBundle() {
BundleMXBean bean = tableModel.getRowBean(bundleTable.getSelectionModel().getMinSelectionIndex());
return bean;
}
class BundleAction extends AbstractAction {
private BundleActionType type;
BundleAction(BundleActionType type) {
this.putValue(NAME, type.toString());
this.type = type;
}
public void actionPerformed(ActionEvent e) {
try {
switch (type) {
case Install:
getFrameworkMXBean().installBundle(bundleURL.getText());
break;
case Start:
getSelectedBundle().start();
break;
case Stop:
getSelectedBundle().stop();
break;
case Update:
getSelectedBundle().update();
break;
case Uninstall:
getSelectedBundle().uninstall();
break;
}
} catch (Exception ex) {
//TODO show friendly UI message...
throw new RuntimeException(ex);
}
}
}
enum BundleActionType {
Install, Start, Stop, Update, Uninstall;
}
}