/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.qpid.management.ui.views.type;
import org.eclipse.jface.preference.PreferenceStore;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.apache.qpid.management.common.mbeans.ManagedBroker;
import org.apache.qpid.management.common.mbeans.ManagedQueue;
import org.apache.qpid.management.ui.ApplicationRegistry;
import org.apache.qpid.management.ui.ManagedBean;
import org.apache.qpid.management.ui.ManagedServer;
import org.apache.qpid.management.ui.jmx.MBeanUtility;
import org.apache.qpid.management.ui.views.MBeanView;
import org.apache.qpid.management.ui.views.NavigationView;
import org.apache.qpid.management.ui.views.ViewUtility;
import static org.apache.qpid.management.common.mbeans.ManagedQueue.*;
import static org.apache.qpid.management.ui.ApplicationRegistry.DATA_DIR;
import static org.apache.qpid.management.ui.Constants.QUEUE;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Semaphore;
public class QueueTypeTabControl extends MBeanTypeTabControl
{
private MBeanServerConnection _mbsc;
private ManagedBroker _vhmb;
private List<String> _selectedAttributes;
private PreferenceStore _preferences;
private Semaphore _tableViewerSemaphore = new Semaphore(1);
private static final String APP_DIR = ApplicationRegistry.DATA_DIR;
private static final String INI_FILENAME = APP_DIR + File.separator + "qpidmc_queue_attributes.ini";
private static final String INI_QUEUE_ATTRIBUES = "QueueAttributesSelection";
private static final ArrayList<String> FALLBACK_ATTRIBUTES_LIST = new ArrayList<String>();
static
{
FALLBACK_ATTRIBUTES_LIST.add(ATTR_NAME);
FALLBACK_ATTRIBUTES_LIST.add(ATTR_ACTIVE_CONSUMER_COUNT);
FALLBACK_ATTRIBUTES_LIST.add(ATTR_AUTODELETE);
FALLBACK_ATTRIBUTES_LIST.add(ATTR_CONSUMER_COUNT);
FALLBACK_ATTRIBUTES_LIST.add(ATTR_DURABLE);
FALLBACK_ATTRIBUTES_LIST.add(ATTR_MAX_MSG_AGE);
FALLBACK_ATTRIBUTES_LIST.add(ATTR_MAX_MSG_COUNT);
FALLBACK_ATTRIBUTES_LIST.add(ATTR_MAX_MSG_SIZE);
FALLBACK_ATTRIBUTES_LIST.add(ATTR_MAX_QUEUE_DEPTH);
FALLBACK_ATTRIBUTES_LIST.add(ATTR_MSG_COUNT);
FALLBACK_ATTRIBUTES_LIST.add(ATTR_OWNER);
FALLBACK_ATTRIBUTES_LIST.add(ATTR_QUEUE_DEPTH);
FALLBACK_ATTRIBUTES_LIST.add(ATTR_RCVD_MSG_COUNT);
}
private static final Map<String, Integer> DEFAULT_COLUMN_WIDTHS = new HashMap<String,Integer>();
static
{
DEFAULT_COLUMN_WIDTHS.put(ATTR_NAME, 215);
DEFAULT_COLUMN_WIDTHS.put(ATTR_OWNER,125);
DEFAULT_COLUMN_WIDTHS.put(ATTR_QUEUE_DEPTH,125);
}
public QueueTypeTabControl(TabFolder tabFolder, ManagedServer server, String virtualHost)
{
super(tabFolder,server,virtualHost,QUEUE);
_mbsc = (MBeanServerConnection) getServerRegistry().getServerConnection();
//create a proxy for the VirtualHostManager mbean to use in retrieving the attribute names/values
_vhmb = MBeanServerInvocationHandler.newProxyInstance(_mbsc,
getVhostMbean().getObjectName(), ManagedBroker.class, false);
}
@Override
protected void init()
{
createIniFileIfNecessary();
loadAttributePreferences();
}
/**
* Create the ini file if it doesn't already exist.
*/
public static void createIniFileIfNecessary()
{
File dir = new File(DATA_DIR);
if (!dir.exists())
{
if(!dir.mkdir())
{
System.err.println("Could not create application data directory " + DATA_DIR);
ViewUtility.popupErrorMessage("Error", "Fatal Error: Unable to create the application data directory: " + DATA_DIR);
System.exit(1);
}
}
File file = new File(INI_FILENAME);
try
{
if (!file.exists())
{
file.createNewFile();
}
}
catch (IOException ex)
{
System.err.println("Error creating the configuration file " + INI_FILENAME);
ViewUtility.popupErrorMessage("Error", "Fatal Error: Unable to create the configuration file: " + INI_FILENAME);
System.exit(1);
}
}
private void loadAttributePreferences()
{
_preferences = new PreferenceStore(INI_FILENAME);
List<String> attributesList = new ArrayList<String>();
//ensure the name is present, and first
attributesList.add(ManagedQueue.ATTR_NAME);
//add any others from the file
try
{
_preferences.load();
String selectedAttributes = _preferences.getString(INI_QUEUE_ATTRIBUES);
if (selectedAttributes.length() != 0)
{
String[] attributes = selectedAttributes.split(",");
for (String attr : attributes)
{
if(attr.equals(ManagedQueue.ATTR_NAME))
{
//the Name attribute is already present
continue;
}
attributesList.add(attr);
}
}
}
catch (IOException e)
{
ViewUtility.popupErrorMessage("Error", "Unable to load previous attribute selections, defaulting to Name only");
System.err.println(e);
}
_selectedAttributes = attributesList;
}
private void saveAttributePreferences()
{
String chosenAttributes = new String();
for(String attr : _selectedAttributes)
{
chosenAttributes = chosenAttributes.concat(attr) + ",";
}
//cut off last ","
int lastIndex = chosenAttributes.lastIndexOf(',');
if (lastIndex != -1)
{
chosenAttributes = chosenAttributes.substring(0,lastIndex);
}
_preferences.putValue(INI_QUEUE_ATTRIBUES, chosenAttributes);
try
{
_preferences.save();
}
catch (IOException e)
{
ViewUtility.popupErrorMessage("Error", "Unable to save the attribute selection, choices will be lost at shutdown");
System.err.println(e);
}
}
@Override
public void refresh(ManagedBean mbean)
{
//Try locking. If we cant aquire the lock, dont bother getting new values.
//Either the attributes are being changed and these values would be out of date,
//or another thread is still in the process of refreshing
if(_tableViewerSemaphore.tryAcquire())
{
try
{
List<List<Object>> values = null;
if(getApiVersion().greaterThanOrEqualTo(1, 3))
{
//Qpid JMX API 1.3+, use this virtualhosts VirtualHostManager MBean
//to retrieve the attributes values requested for all queues at once
try
{
values = _vhmb.retrieveQueueAttributeValues(_selectedAttributes.toArray(new String[0]));
}
catch(Exception e)
{
MBeanUtility.handleException(getVhostMbean(), e);
}
}
else
{
//Qpid JMX API 1.2 or below, use the local ManagedBeans and look
//up the attribute values for each queue individually
setMbeanList(getMbeans());
values = MBeanUtility.getQueueAttributes(getMbeanList(), _selectedAttributes.toArray(new String[0]));
}
getTableViewer().setInput(values);
layout();
}
finally
{
_tableViewerSemaphore.release();
}
}
}
@Override
protected List<ManagedBean> getMbeans()
{
return getServerRegistry().getQueues(getVirtualHost());
}
private void clearTableComposite()
{
ViewUtility.disposeChildren(getTableComposite());
}
@Override
protected void createTable()
{
setTable(new Table (getTableComposite(), SWT.MULTI | SWT.SCROLL_LINE | SWT.BORDER | SWT.FULL_SELECTION));
getTable().setLinesVisible(true);
getTable().setHeaderVisible(true);
GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
getTable().setLayoutData(data);
setTableViewer(new TableViewer(getTable()));
final QueueTableSorter tableSorter = new QueueTableSorter();
for (int i = 0; i < _selectedAttributes.size(); i++)
{
final int index = i;
final TableViewerColumn viewerColumn = new TableViewerColumn(getTableViewer(), SWT.NONE);
final TableColumn column = viewerColumn.getColumn();
String attrName = _selectedAttributes.get(i);
column.setMoveable(true);
column.setText(attrName);
column.pack();
if(DEFAULT_COLUMN_WIDTHS.containsKey(attrName))
{
//retrieve the desired default width
column.setWidth(DEFAULT_COLUMN_WIDTHS.get(attrName));
}
else
{
//add padding for sort direction indicator
column.setWidth(column.getWidth() + 15);
}
column.setResizable(true);
//Setting the right sorter
column.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
tableSorter.setColumn(index);
final TableViewer viewer = getTableViewer();
int dir = viewer .getTable().getSortDirection();
if (viewer.getTable().getSortColumn() == column)
{
dir = dir == SWT.UP ? SWT.DOWN : SWT.UP;
}
else
{
dir = SWT.UP;
}
viewer.getTable().setSortDirection(dir);
viewer.getTable().setSortColumn(column);
viewer.refresh();
}
});
}
getTableViewer().setContentProvider(new QueueContentProviderImpl());
getTableViewer().setLabelProvider(new QueueLabelProviderImpl());
getTableViewer().setUseHashlookup(true);
getTableViewer().setSorter(tableSorter);
getTable().setSortColumn(getTable().getColumn(0));
getTable().setSortDirection(SWT.UP);
addTableListeners();
}
protected void createLowerAreaButton(Composite parent)
{
Composite lowerButtonComposite = getToolkit().createComposite(parent, SWT.NONE);
GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
lowerButtonComposite.setLayoutData(gridData);
lowerButtonComposite.setLayout(new GridLayout());
final Button attributesButton = getToolkit().createButton(lowerButtonComposite, "Select Attributes ...", SWT.PUSH);
gridData = new GridData(SWT.RIGHT, SWT.CENTER, true, false);
attributesButton.setLayoutData(gridData);
attributesButton.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent e)
{
chooseAttributes(attributesButton.getShell());
}
});
}
private void chooseAttributes(final Shell parent)
{
List<String> availableAttributes;
if(getApiVersion().greaterThanOrEqualTo(1, 3))
{
//Qpid JMX API 1.3+, request the current queue attributes names from the broker
try
{
availableAttributes = _vhmb.retrieveQueueAttributeNames();
}
catch (IOException e)
{
availableAttributes = new ArrayList<String>(FALLBACK_ATTRIBUTES_LIST);
}
}
else
{
//Qpid JMX API 1.2 or below, use the falllback list of names.
availableAttributes = new ArrayList<String>(FALLBACK_ATTRIBUTES_LIST);
}
final List<String> chosenAttributes = new ArrayList<String>();
final Shell shell = ViewUtility.createModalDialogShell(parent, "Select Attributes");
Composite attributesComposite = getToolkit().createComposite(shell, SWT.NONE);
attributesComposite.setBackground(shell.getBackground());
attributesComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
attributesComposite.setLayout(new GridLayout(2,false));
//add a selected-but-disabled check box for the Name attribute (its a mandatory attribute)
final Button nameCheckbox = new Button(attributesComposite, SWT.CHECK);
nameCheckbox.setText(ManagedQueue.ATTR_NAME);
nameCheckbox.setSelection(true);
nameCheckbox.setEnabled(false);
for(String attr : availableAttributes)
{
if(attr.equals(ManagedQueue.ATTR_NAME))
{
//Name attribute is mandatory and gets added to the front of the list later
continue;
}
final Button attrButton = new Button(attributesComposite, SWT.CHECK);
attrButton.setText(attr);
//if it was checked before, select it again now
if(_selectedAttributes.contains(attr))
{
attrButton.setSelection(true);
chosenAttributes.add(attr);
}
//add a selection listener to update the selected attribute list
attrButton.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent e)
{
if(attrButton.getSelection())
{
chosenAttributes.add(attrButton.getText());
}
else
{
chosenAttributes.remove(attrButton.getText());
}
}
});
}
Composite okCancelButtonsComp = getToolkit().createComposite(shell);
okCancelButtonsComp.setBackground(shell.getBackground());
okCancelButtonsComp.setLayoutData(new GridData(SWT.RIGHT, SWT.FILL, true, true));
okCancelButtonsComp.setLayout(new GridLayout(2,false));
Button okButton = getToolkit().createButton(okCancelButtonsComp, "OK", SWT.PUSH);
okButton.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false));
Button cancelButton = getToolkit().createButton(okCancelButtonsComp, "Cancel", SWT.PUSH);
cancelButton.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false));
okButton.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent e)
{
shell.dispose();
//The Name attribute is mandatory, add it now, also
//ensuring it is left-most by placing it first in the list
List<String> newSelection = new ArrayList<String>();
newSelection.add(ManagedQueue.ATTR_NAME);
//now add all remaining choices in alphabetical order
Collections.sort(chosenAttributes);
newSelection.addAll(chosenAttributes);
_tableViewerSemaphore.acquireUninterruptibly();
try
{
_selectedAttributes = newSelection;
clearTableComposite();
createTable();
saveAttributePreferences();
}
finally
{
_tableViewerSemaphore.release();
}
refresh(_mbean);
}
});
cancelButton.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent e)
{
shell.dispose();
}
});
shell.setDefaultButton(okButton);
shell.pack();
ViewUtility.centerChildInParentShell(parent, shell);
shell.open();
}
private String getQueueDepthString(Long value)
{
if(value == null)
{
return "-";
}
if (getApiVersion().greaterThanOrEqualTo(1, 2))
{
//Qpid JMX API 1.2 or above, returns Bytes
return convertLongBytesToText(value);
}
else
{
//Qpid JMX API 1.1 or below, returns KB
double mb = 1024.0;
if(value > mb) //MB
{
return String.format("%.3f", (Double)(value / mb)) + " MB";
}
else //KB
{
return value + " KB";
}
}
}
private String convertLongBytesToText(Long value)
{
if(value == null)
{
return "-";
}
double mb = 1024.0 * 1024.0;
double kb = 1024.0;
if(value >= mb) //MB
{
return String.format("%.3f", (Double)((double)value / mb)) + " MB";
}
else if (value >= kb) //KB
{
return String.format("%.3f", (Double)((double)value / kb)) + " KB";
}
else //Bytes
{
return value + " Bytes";
}
}
/**
* sorter class for the table viewer.
*
*/
private static class QueueTableSorter extends ViewerSorter
{
protected int column;
protected static final int ASCENDING = 0;
protected static final int DESCENDING = 1;
protected int direction;
public QueueTableSorter()
{
this.column = 0;
direction = ASCENDING;
}
public void setColumn(int column)
{
if(column == this.column)
{
// Same column as last sort; toggle the direction
direction = 1 - direction;
}
else
{
// New column; do an ascending sort
this.column = column;
direction = ASCENDING;
}
}
@SuppressWarnings("unchecked")
@Override
public int compare(Viewer viewer, Object e1, Object e2)
{
List<Object> queue1 = (List<Object>) e1;
List<Object> queue2 = (List<Object>) e2;
int comparison = 0;
switch(column)
{
default:
if(queue1.get(column) instanceof Comparable)
{
comparison = ((Comparable)queue1.get(column)).compareTo((Comparable) queue2.get(column));
}
}
// If descending order, flip the direction
if(direction == DESCENDING)
{
comparison = -comparison;
}
return comparison;
}
}
/**
* Content Provider class for the table viewer for Qpid JMX API 1.3 and above.
*/
private static class QueueContentProviderImpl implements IStructuredContentProvider
{
public void inputChanged(Viewer v, Object oldInput, Object newInput)
{
}
public void dispose()
{
}
@SuppressWarnings("unchecked")
public Object[] getElements(Object parent)
{
return ((List<List<Object>>) parent).toArray();
}
}
/**
* Label Provider class for the table viewer for for Qpid JMX API 1.3 and above.
*/
private class QueueLabelProviderImpl extends LabelProvider implements ITableLabelProvider
{
@SuppressWarnings("unchecked")
public String getColumnText(Object element, int columnIndex)
{
List<Object> attributes = (List<Object>) element;
switch (columnIndex)
{
default :
String attrName = _selectedAttributes.get(columnIndex);
if(ATTR_QUEUE_DEPTH.equals(attrName))
{
return getQueueDepthString((Long) attributes.get(columnIndex));
}
else if(ATTR_MAX_QUEUE_DEPTH.equals(attrName) || ATTR_MAX_MSG_SIZE.equals(attrName))
{
Number val = (Number)attributes.get(columnIndex);
return convertLongBytesToText(val.longValue());
}
else if(ATTR_MAX_MSG_AGE.equals(attrName))
{
return String.valueOf(attributes.get(columnIndex) + "ms");
}
return String.valueOf(attributes.get(columnIndex));
}
}
public Image getColumnImage(Object element, int columnIndex)
{
return null;
}
}
@SuppressWarnings("unchecked")
@Override
protected void addMBeanToFavourites()
{
int selectionIndex = getTable().getSelectionIndex();
if (selectionIndex == -1)
{
return;
}
int[] selectedIndices = getTable().getSelectionIndices();
ArrayList<ManagedBean> selectedMBeans = new ArrayList<ManagedBean>();
boolean allSucceded = true;
//the entries are created from an List<Object> with the attribute values (name first)
for(int index = 0; index < selectedIndices.length ; index++)
{
List<Object> queueEntry = (List<Object>) getTable().getItem(selectedIndices[index]).getData();
String queueName = (String) queueEntry.get(0);
ManagedBean queueMBean = getServerRegistry().getQueue(queueName, getVirtualHost());
//check queue had not already been unregistered before trying to add it
if(queueMBean != null)
{
selectedMBeans.add(queueMBean);
}
else
{
allSucceded = false;
}
}
if(allSucceded)
{
//ensure the status bar is cleared of any previous failures added by the below
ViewUtility.clearStatusBar();
}
else
{
ViewUtility.operationFailedStatusBarMessage("A selected queue could not be added as it was no longer registered");
refresh();
}
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
NavigationView view = (NavigationView)window.getActivePage().findView(NavigationView.ID);
ManagedBean bean = null;
try
{
for(ManagedBean mbean: selectedMBeans)
{
bean = mbean;
view.addManagedBean(mbean);
}
}
catch (Exception ex)
{
MBeanUtility.handleException(bean, ex);
}
}
@SuppressWarnings("unchecked")
@Override
protected void openMBean()
{
int selectionIndex = getTable().getSelectionIndex();
if (selectionIndex == -1)
{
return;
}
ManagedBean selectedMBean;
//the entries are created from an List<Object> with the attribute values (name first)
List<Object> queueEntry = (List<Object>) getTable().getItem(selectionIndex).getData();
String queueName = (String) queueEntry.get(0);
selectedMBean = getServerRegistry().getQueue(queueName, getVirtualHost());
if(selectedMBean == null)
{
ViewUtility.popupErrorMessage("Error", "Unable to retrieve the selected MBean to open it");
return;
}
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
MBeanView view = (MBeanView) window.getActivePage().findView(MBeanView.ID);
try
{
view.openMBean(selectedMBean);
}
catch (Exception ex)
{
MBeanUtility.handleException(selectedMBean, ex);
}
}
}