blob: c98b5731e5619b889aae1f194d070fba3e277bb1 [file] [log] [blame]
/*
*
* 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.queue;
import static org.apache.qpid.management.ui.Constants.CONSOLE_IMAGE;
import static org.apache.qpid.management.ui.Constants.RESULT;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.openmbean.TabularDataSupport;
import org.apache.qpid.management.ui.ApplicationRegistry;
import org.apache.qpid.management.ui.ManagedBean;
import org.apache.qpid.management.common.mbeans.ManagedQueue;
import org.apache.qpid.management.ui.jmx.JMXManagedObject;
import org.apache.qpid.management.ui.jmx.MBeanUtility;
import org.apache.qpid.management.ui.views.TabControl;
import org.apache.qpid.management.ui.views.ViewUtility;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
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.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
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.swt.widgets.Text;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
/**
* Control class for the Queue mbean Operations tab.
*/
public class QueueOperationsTabControl extends TabControl
{
private FormToolkit _toolkit;
private ScrolledForm _form;
private Table _table = null;
private TableViewer _tableViewer = null;
private Composite _paramsComposite = null;
private TabularDataSupport _messages = null;
private ManagedQueue _qmb;
static final String MSG_AMQ_ID = ManagedQueue.VIEW_MSGS_COMPOSITE_ITEM_NAMES[0];
static final String MSG_HEADER = ManagedQueue.VIEW_MSGS_COMPOSITE_ITEM_NAMES[1];
static final String MSG_SIZE = ManagedQueue.VIEW_MSGS_COMPOSITE_ITEM_NAMES[2];
static final String MSG_REDELIVERED = ManagedQueue.VIEW_MSGS_COMPOSITE_ITEM_NAMES[3];
public QueueOperationsTabControl(TabFolder tabFolder, JMXManagedObject mbean, MBeanServerConnection mbsc)
{
super(tabFolder);
_mbean = mbean;
_qmb = (ManagedQueue) MBeanServerInvocationHandler.newProxyInstance(mbsc,
mbean.getObjectName(), ManagedQueue.class, false);
_toolkit = new FormToolkit(_tabFolder.getDisplay());
_form = _toolkit.createScrolledForm(_tabFolder);
_form.getBody().setLayout(new GridLayout());
createComposites();
createWidgets();
}
private void createComposites()
{
_paramsComposite = _toolkit.createComposite(_form.getBody(), SWT.NONE);
_paramsComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
_paramsComposite.setLayout(new GridLayout());
}
/**
* @see TabControl#getControl()
*/
public Control getControl()
{
return _form;
}
/**
* @see TabControl#setFocus()
*/
public void setFocus()
{
_table.setFocus();
}
@Override
public void refresh(ManagedBean mbean)
{
_mbean = mbean;
if (_mbean == null)
{
_tableViewer.setInput(null);
return;
}
_messages = null;
try
{
//gather a list of all messages on the queue for display and selection
_messages = (TabularDataSupport) _qmb.viewMessages(1,Integer.MAX_VALUE);
//TODO - viewMessages takes int args, limiting number of messages which can be viewed
//to the first 2^32 messages on the queue at the time of invocation.
//For consistency with other methods, expand values to Long by introducing a new method.
//Use AMQ ID or current 'position in queue' numbering scheme ??
}
catch (Exception e)
{
MBeanUtility.handleException(mbean,e);
}
_form.setVisible(false);
_tableViewer.setInput(_messages);
_form.setVisible(true);
layout();
}
public void layout()
{
_form.layout(true);
_form.getBody().layout(true, true);
}
private void createWidgets()
{
Group messagesGroup = new Group(_paramsComposite, SWT.SHADOW_NONE | SWT.SCROLL_LINE);
messagesGroup.setBackground(_paramsComposite.getBackground());
messagesGroup.setText("Messages");
messagesGroup.setLayout(new GridLayout());
GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
messagesGroup.setLayoutData(gridData);
Composite tableAndButtonsComposite = _toolkit.createComposite(messagesGroup);
gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
gridData.minimumHeight = 220;
gridData.heightHint = 220;
tableAndButtonsComposite.setLayoutData(gridData);
tableAndButtonsComposite.setLayout(new GridLayout(2,false));
_table = new Table (tableAndButtonsComposite, SWT.MULTI | SWT.SCROLL_LINE | SWT.BORDER | SWT.FULL_SELECTION);
_table.setLinesVisible (true);
_table.setHeaderVisible (true);
GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
_table.setLayoutData(data);
_tableViewer = new TableViewer(_table);
final TableSorter tableSorter = new TableSorter();
String[] titles = {"AMQ ID", "Size(bytes)"};
int[] bounds = { 175, 175 };
for (int i = 0; i < titles.length; i++)
{
final int index = i;
final TableColumn column = new TableColumn (_table, SWT.NONE);
column.setText(titles[i]);
column.setWidth(bounds[i]);
column.setResizable(true);
//Setting the right sorter
column.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
tableSorter.setColumn(index);
final TableViewer viewer = _tableViewer;
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();
}
});
}
_tableViewer.setContentProvider(new ContentProviderImpl());
_tableViewer.setLabelProvider(new LabelProviderImpl());
_tableViewer.setSorter(tableSorter);
//Side Buttons
Composite buttonsComposite = _toolkit.createComposite(tableAndButtonsComposite);
gridData = new GridData(SWT.FILL, SWT.FILL, false, true);
buttonsComposite.setLayoutData(gridData);
buttonsComposite.setLayout(new GridLayout());
final Button viewSelectedMsgButton = _toolkit.createButton(buttonsComposite, "View Message Content ...", SWT.PUSH);
viewSelectedMsgButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
viewSelectedMsgButton.setEnabled(false);
viewSelectedMsgButton.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent e)
{
if (_table.getSelectionIndex() == -1)
{
return;
}
viewMessageContent();
}
});
final Button moveMessagesButton = _toolkit.createButton(buttonsComposite, "Move Message(s) ...", SWT.PUSH);
moveMessagesButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
moveMessagesButton.setEnabled(false);
moveMessagesButton.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent e)
{
if (_table.getSelectionIndex() == -1)
{
return;
}
moveMessages(moveMessagesButton.getShell());
}
});
final Button clearQueueButton = _toolkit.createButton(buttonsComposite, "Clear Queue", SWT.PUSH);
clearQueueButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
clearQueueButton.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent e)
{
int response = ViewUtility.popupOkCancelConfirmationMessage("Clear Queue",
"Clear queue ?");
if (response == SWT.OK)
{
try
{
_qmb.clearQueue();
}
catch (Exception e2)
{
MBeanUtility.handleException(_mbean, e2);
}
//TODO:display result
refresh(_mbean);;
}
}
});
_toolkit.createLabel(messagesGroup, "Message Header: ");
//Redelivered status and header
Composite headerEtcComposite = _toolkit.createComposite(messagesGroup);
gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
headerEtcComposite.setLayoutData(gridData);
headerEtcComposite.setLayout(new GridLayout());
final Text headerText = new Text(headerEtcComposite, SWT.WRAP | SWT.BORDER );
headerText.setText("Select a message to view its header.");
headerText.setEditable(false);
data = new GridData(SWT.LEFT, SWT.TOP, false, false);
data.minimumHeight = 230;
data.heightHint = 230;
data.minimumWidth = 500;
data.widthHint = 500;
headerText.setLayoutData(data);
Composite redeliveryComposite = _toolkit.createComposite(headerEtcComposite);
redeliveryComposite.setLayout(new GridLayout(2,false));
data = new GridData(SWT.LEFT, SWT.FILL, false, false);
data.minimumWidth = 150;
data.widthHint = 150;
redeliveryComposite.setLayoutData(data);
_toolkit.createLabel(redeliveryComposite, "Redelivered: ");
final Text redeliveredText = new Text(redeliveryComposite, SWT.BORDER);
redeliveredText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
redeliveredText.setText("-");
redeliveredText.setEditable(false);
//listener for double clicking to view message content
_table.addMouseListener(new MouseListener()
{
// MouseListener implementation
public void mouseDoubleClick(MouseEvent event)
{
viewMessageContent();
}
public void mouseDown(MouseEvent e){}
public void mouseUp(MouseEvent e){}
});
//selection listener to enable and disable buttons, update header and redelivered info
_tableViewer.addSelectionChangedListener(new ISelectionChangedListener(){
public void selectionChanged(SelectionChangedEvent evt)
{
int selectionIndex = _table.getSelectionIndex();
if (selectionIndex == -1)
{
headerText.setText("Select a message to view its header.");
redeliveredText.setText("-");
viewSelectedMsgButton.setEnabled(false);
moveMessagesButton.setEnabled(false);
return;
}
else
{
moveMessagesButton.setEnabled(true);
final CompositeData selectedMsg = (CompositeData)_table.getItem(selectionIndex).getData();
Boolean redelivered = (Boolean) selectedMsg.get(MSG_REDELIVERED);
redeliveredText.setText(redelivered.toString());
String[] msgHeader = (String[]) selectedMsg.get(MSG_HEADER);
headerText.setText("");
for(String s: msgHeader)
{
headerText.append(s + "\n");
}
}
if (_table.getSelectionCount() > 1)
{
viewSelectedMsgButton.setEnabled(false);
}
else
{
viewSelectedMsgButton.setEnabled(true);
}
}
});
}
/**
* Content Provider class for the table viewer
*/
private class ContentProviderImpl implements IStructuredContentProvider
{
public void inputChanged(Viewer v, Object oldInput, Object newInput)
{
}
public void dispose()
{
}
public Object[] getElements(Object parent)
{
Collection<Object> rowCollection = ((TabularDataSupport) parent).values();
return rowCollection.toArray();
}
}
/**
* Label Provider class for the table viewer
*/
private class LabelProviderImpl extends LabelProvider implements ITableLabelProvider
{
@Override
public String getColumnText(Object element, int columnIndex)
{
switch (columnIndex)
{
case 0 : // msg id column
return String.valueOf(((CompositeDataSupport) element).get(MSG_AMQ_ID));
case 1 : // msg size column
return String.valueOf(((CompositeDataSupport) element).get(MSG_SIZE));
default :
return "-";
}
}
@Override
public Image getColumnImage(Object element, int columnIndex)
{
return null;
}
}
/**
* Sorter class for the table viewer.
*
*/
public class TableSorter extends ViewerSorter
{
private int column;
private static final int ASCENDING = 0;
private static final int DESCENDING = 1;
private int direction = DESCENDING;
public TableSorter()
{
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;
}
}
@Override
public int compare(Viewer viewer, Object e1, Object e2)
{
CompositeData msg1 = (CompositeData) e1;
CompositeData msg2 = (CompositeData) e2;
int comparison = 0;
switch(column)
{
case 0:
comparison = ((Long) msg1.get(MSG_AMQ_ID)).compareTo((Long)msg2.get(MSG_AMQ_ID));
break;
case 1:
comparison = ((Long) msg1.get(MSG_SIZE)).compareTo((Long)msg2.get(MSG_SIZE));
break;
default:
comparison = 0;
}
// If descending order, flip the direction
if(direction == DESCENDING)
{
comparison = -comparison;
}
return comparison;
}
}
private void viewMessageContent()
{
int selectionIndex = _table.getSelectionIndex();
if (selectionIndex != -1)
{
try
{
final CompositeData selectedMsg = (CompositeData)_table.getItem(selectionIndex).getData();
Long msgId = (Long) selectedMsg.get(MSG_AMQ_ID);
Object result = _qmb.viewMessageContent(msgId);
populateResults(result);
}
catch (Exception e3)
{
MBeanUtility.handleException(_mbean, e3);
}
}
}
private void populateResults(Object result)
{
Display display = Display.getCurrent();
int width = 610;
int height = 400;
Shell shell = ViewUtility.createPopupShell(RESULT, width, height);
shell.setImage(ApplicationRegistry.getImage(CONSOLE_IMAGE));
ViewUtility.populateCompositeWithData(_toolkit, shell, result);
shell.open();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
{
display.sleep();
}
}
shell.dispose();
}
private void moveMessages(final Shell parent)
{
final ArrayList<Long> rangeStarts = new ArrayList<Long>();
final ArrayList<Long> rangeEnds = new ArrayList<Long>();
gatherSelectedAMQMsgIDRanges(rangeStarts,rangeEnds);
String rangeString = getRangesString(rangeStarts,rangeEnds);
final Shell shell = ViewUtility.createModalDialogShell(parent, "Move Messages");
Composite idComposite = _toolkit.createComposite(shell, SWT.NONE);
idComposite.setBackground(shell.getBackground());
idComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
idComposite.setLayout(new GridLayout());
_toolkit.createLabel(idComposite,"Move message(s) with AMQ ID:").setBackground(shell.getBackground());
_toolkit.createLabel(idComposite,rangeString).setBackground(shell.getBackground());
Composite destinationComposite = _toolkit.createComposite(shell, SWT.NONE);
destinationComposite.setBackground(shell.getBackground());
destinationComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
destinationComposite.setLayout(new GridLayout(2,false));
_toolkit.createLabel(destinationComposite,"To Queue:").setBackground(shell.getBackground());
final Combo destinationCombo = new Combo(destinationComposite,SWT.NONE);
destinationCombo.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
Composite okCancelButtonsComp = _toolkit.createComposite(shell);
okCancelButtonsComp.setBackground(shell.getBackground());
okCancelButtonsComp.setLayoutData(new GridData(SWT.RIGHT, SWT.FILL, true, true));
okCancelButtonsComp.setLayout(new GridLayout(2,false));
Button okButton = _toolkit.createButton(okCancelButtonsComp, "OK", SWT.PUSH);
okButton.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false));
Button cancelButton = _toolkit.createButton(okCancelButtonsComp, "Cancel", SWT.PUSH);
cancelButton.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false));
List<String> queueList = ApplicationRegistry.getServerRegistry(_mbean).getQueueNames(_mbean.getVirtualHostName());
queueList.remove(_mbean.getName());
if(queueList.size() == 0)
{
destinationCombo.setItems(new String[]{"No other queues available"});
okButton.setEnabled(false);
}
else
{
destinationCombo.setItems(queueList.toArray(new String[0]));
}
destinationCombo.select(0);
okButton.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent e)
{
String destQueue = destinationCombo.getItem(destinationCombo.getSelectionIndex()).toString();
shell.dispose();
try
{
for(int i=0 ; i < rangeStarts.size() ; i++)
{
Long from = rangeStarts.get(i);
Long to = rangeEnds.get(i);
_qmb.moveMessages(Long.valueOf(from), Long.valueOf(to), destQueue);
}
}
catch (Exception e4)
{
MBeanUtility.handleException(_mbean, e4);
}
//TODO: display result?
refresh(_mbean);
}
});
cancelButton.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent e)
{
shell.dispose();
}
});
shell.setDefaultButton(okButton);
shell.pack();
shell.open();
}
private void gatherSelectedAMQMsgIDRanges(ArrayList<Long> starts, ArrayList<Long> ends)
{
SortedSet<Long> amqIDs = new TreeSet<Long>();
for(Integer i : _table.getSelectionIndices())
{
CompositeData selectedMsg = (CompositeData)_table.getItem(i).getData();
amqIDs.add((Long)selectedMsg.get(MSG_AMQ_ID));
}
//initialise the first range
Long start = amqIDs.first();
Long end = amqIDs.first();
for(Long id : amqIDs)
{
if(id == amqIDs.first())
{
//skip first check, already initialised range
continue;
}
if(id == end +1)
{
//part of previous range, append
end = id;
}
else
{
//not in previous range, record existing start and end msg id values
starts.add(start);
ends.add(end);
//begin new range with this msg id
start = id;
end = id;
}
}
//record the last range created
starts.add(start);
ends.add(end);
}
private String getRangesString(ArrayList<Long> starts, ArrayList<Long> ends)
{
String idRangesString = new String("");
for(int i=0 ; i < starts.size() ; i++)
{
long start = starts.get(i);
long end = ends.get(i);
if(i != 0)
{
idRangesString = idRangesString.concat(", ");
}
if(start == end)
{
idRangesString = idRangesString.concat(String.valueOf(starts.get(i)));
}
else
{
idRangesString = idRangesString.concat(starts.get(i) + "-" + ends.get(i));
}
}
return idRangesString.concat(".");
}
}