blob: 5866dcab4ab2d38022495a75060b71d8ebebb99a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2021 SAP AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* SAP AG - initial API and implementation
* IBM Corporation - refactor for new/import wizard as WizardPage,
* rename from ProviderConfigurationDialog
*******************************************************************************/
package org.eclipse.mat.ui.internal.acquire;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.wizard.IWizardPage;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.internal.acquire.HeapDumpProviderDescriptor;
import org.eclipse.mat.internal.acquire.HeapDumpProviderRegistry;
import org.eclipse.mat.internal.acquire.VmInfoDescriptor;
import org.eclipse.mat.query.annotations.descriptors.IAnnotatedObjectDescriptor;
import org.eclipse.mat.query.registry.AnnotatedObjectArgumentsSet;
import org.eclipse.mat.query.registry.ArgumentDescriptor;
import org.eclipse.mat.query.registry.ArgumentFactory;
import org.eclipse.mat.snapshot.acquire.IHeapDumpProvider;
import org.eclipse.mat.ui.Messages;
import org.eclipse.mat.ui.accessibility.AccessibleCompositeAdapter;
import org.eclipse.mat.ui.internal.acquire.AcquireDialog.ProcessSelectionListener;
import org.eclipse.mat.ui.internal.acquire.ProviderArgumentsTable.ITableListener;
import org.eclipse.mat.ui.internal.browser.QueryContextHelp;
import org.eclipse.mat.util.IProgressListener;
import org.eclipse.mat.util.MessageUtil;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.PlatformUI;
/**
* Handles configuring arguments for a particular dump provider.
*
*/
public class ProviderConfigurationWizardPage extends WizardPage implements ITableListener, ProcessSelectionListener
{
private ProviderArgumentsTable table;
private Table availableProvidersTable;
private AcquireDialog acquireDialog;
private QueryContextHelp helpPopup;
// Has a provider been changed in the UI
private boolean changed;
// Has that change been applied to the provider
private boolean applied;
public ProviderConfigurationWizardPage(AcquireDialog acquireDialog)
{
super(Messages.ProviderConfigurationDialog_ConfigureProvidersDialogTitle, Messages.ProviderConfigurationDialog_ConfigureProvidersDialogTitle, null);
this.acquireDialog = acquireDialog;
}
//@Override
public void createControl(Composite parent)
{
Composite composite = new Composite(parent, SWT.RESIZE);
composite.setLayout(new GridLayout(1, false));
GridDataFactory.fillDefaults().grab(true, true).span(1, 1).applyTo(composite);
Label providersLabel = new Label(composite, SWT.NONE);
providersLabel.setText(Messages.ProviderConfigurationDialog_AvailableProvidersLabel);
GridDataFactory.swtDefaults().span(1, 1).applyTo(providersLabel);
createProvidersTable(composite);
Label argumentsLabel = new Label(composite, SWT.NONE);
argumentsLabel.setText(Messages.ProviderConfigurationDialog_ConfigurableParameteresLabel);
GridDataFactory.swtDefaults().span(1, 1).applyTo(argumentsLabel);
createArgumentsTable(composite);
availableProvidersTable.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e)
{
if (e.item instanceof TableItem)
{
AnnotatedObjectArgumentsSet argumentsSet = (AnnotatedObjectArgumentsSet) ((TableItem) e.item).getData();
table.providerSelected(argumentsSet);
onFocus(null);
relocateHelp(true);
}
}
});
// If a process is selected, make the configuration provider match the process
acquireDialog.addProcessSelectionListener(this);
PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, "org.eclipse.mat.ui.help.query_arguments"); //$NON-NLS-1$
Listener listener = new Listener()
{
public void handleEvent(Event event)
{
relocateHelp(false);
}
};
getShell().addListener(SWT.Resize, listener);
getShell().addListener(SWT.Move, listener);
setControl(composite);
}
public IWizardPage getNextPage() {
try
{
if (changed)
{
applied = true;
applyChanges();
onError(null);
}
}
catch (SnapshotException e)
{
onError(e.getLocalizedMessage());
return null;
}
return super.getNextPage();
}
/*
* getPreviousPage() gets called whenever updateButtons is called
* so it isn't useful for things just before we go back
*/
private void applyChanges() throws SnapshotException
{
AnnotatedObjectArgumentsSet currentSet = table.getArgumentSet();
if (currentSet != null)
{
// Throw exception if current selection fails
applyProviderChanges(currentSet);
}
for (TableItem item : availableProvidersTable.getItems())
{
AnnotatedObjectArgumentsSet argumentSet = (AnnotatedObjectArgumentsSet) item.getData();
if (argumentSet != currentSet)
{
try
{
applyProviderChanges(argumentSet);
}
catch (SnapshotException e)
{
// Ignore errors from non-selected providers
}
}
}
}
private void applyProviderChanges(AnnotatedObjectArgumentsSet argumentSet) throws SnapshotException
{
try
{
HeapDumpProviderDescriptor providerDescriptor = (HeapDumpProviderDescriptor) argumentSet.getDescriptor();
IHeapDumpProvider impl = providerDescriptor.getHeapDumpProvider();
for (ArgumentDescriptor parameter : providerDescriptor.getArguments())
{
Object value = argumentSet.getArgumentValue(parameter);
if (value == null && parameter.isMandatory())
{
value = parameter.getDefaultValue();
if (value == null)
throw new SnapshotException(MessageUtil.format(
Messages.ProviderConfigurationDialog_MissingParameterErrorMessage, parameter.getName()));
}
if (value == null)
{
if (argumentSet.getValues().containsKey(parameter))
{
Logger.getLogger(getClass().getName()).log(Level.INFO,
MessageUtil.format("Setting null value for: {0}", parameter.getName())); //$NON-NLS-1$
parameter.getField().set(impl, null);
}
continue;
}
try
{
if (value instanceof ArgumentFactory)
{
parameter.getField().set(impl, ((ArgumentFactory) value).build(parameter));
}
else if (parameter.isArray())
{
List<?> list = (List<?>) value;
Object array = Array.newInstance(parameter.getType(), list.size());
int ii = 0;
for (Object v : list)
Array.set(array, ii++, v);
parameter.getField().set(impl, array);
}
else if (parameter.isList())
{
// handle ArgumentFactory inside list
List<?> source = (List<?>) value;
List<Object> target = new ArrayList<Object>(source.size());
for (int ii = 0; ii < source.size(); ii++)
{
Object v = source.get(ii);
if (v instanceof ArgumentFactory)
v = ((ArgumentFactory) v).build(parameter);
target.add(v);
}
parameter.getField().set(impl, target);
}
else
{
parameter.getField().set(impl, value);
}
}
catch (IllegalArgumentException e)
{
throw new SnapshotException(MessageUtil.format(Messages.ProviderConfigurationDialog_IllegalArgumentErrorMessage, value,
value.getClass().getName(), parameter.getName(), parameter.getType().getName()), e);
}
catch (IllegalAccessException e)
{
// should not happen as we check accessibility when
// registering queries
throw new SnapshotException(MessageUtil.format("Unable to access field {0} of type {1}", parameter //$NON-NLS-1$
.getName(), parameter.getType().getName()), e);
}
}
}
catch (IProgressListener.OperationCanceledException e)
{
throw e; // no nesting!
}
catch (SnapshotException e)
{
throw e; // no nesting!
}
catch (Exception e)
{
throw SnapshotException.rethrow(e);
}
}
private void createProvidersTable(Composite parent)
{
Composite tableComposite = new Composite(parent, SWT.NONE);
GridDataFactory.fillDefaults().grab(true, false).indent(0, 0).applyTo(tableComposite);
TableColumnLayout tableColumnLayout = new TableColumnLayout();
tableComposite.setLayout(tableColumnLayout);
availableProvidersTable = new Table(tableComposite, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION);
TableColumn column1 = new TableColumn(availableProvidersTable, SWT.NONE);
column1.setText(Messages.ProviderConfigurationDialog_NameColumnHeader);
tableColumnLayout.setColumnData(column1, new ColumnWeightData(0, 200));
TableColumn column2 = new TableColumn(availableProvidersTable, SWT.NONE);
column2.setText(Messages.ProviderConfigurationDialog_DescriptionColumnHeader);
tableColumnLayout.setColumnData(column2, new ColumnWeightData(100, 250));
availableProvidersTable.setHeaderVisible(true);
availableProvidersTable.setLinesVisible(true);
AccessibleCompositeAdapter.access(availableProvidersTable);
Collection<HeapDumpProviderDescriptor> providers = HeapDumpProviderRegistry.instance().getHeapDumpProviders();
for (HeapDumpProviderDescriptor heapDumpProviderDescriptor : providers)
{
TableItem item = new TableItem(availableProvidersTable, SWT.NONE, 0);
item.setData(new AnnotatedObjectArgumentsSet(heapDumpProviderDescriptor));
item.setText(0, heapDumpProviderDescriptor.getName());
if (heapDumpProviderDescriptor.getHelp() != null)
item.setText(1, heapDumpProviderDescriptor.getHelp());
}
tableComposite.layout();
tableComposite.pack();
}
private void createArgumentsTable(Composite parent)
{
ScrolledComposite composite = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER);
composite.setLayout(new GridLayout());
composite.setExpandHorizontal(true);
composite.setExpandVertical(true);
GridDataFactory.fillDefaults().grab(true, true).applyTo(composite);
Composite tableComposite = new Composite(composite, SWT.NONE);
GridDataFactory.fillDefaults().grab(true, true).indent(0, 0).applyTo(tableComposite);
Dialog.applyDialogFont(composite);
table = new ProviderArgumentsTable(tableComposite, SWT.FULL_SELECTION | SWT.SINGLE);
table.addListener(this);
tableComposite.layout();
tableComposite.pack();
composite.setContent(tableComposite);
GridDataFactory.fillDefaults().hint(300, 100).grab(true, true).span(1, 2).applyTo(composite);
}
public void relocateHelp(final boolean create)
{
final AnnotatedObjectArgumentsSet argumentSet = table.getArgumentSet();
if (argumentSet == null) return;
if (argumentSet.getDescriptor().isHelpAvailable() && //
(create || (helpPopup != null && helpPopup.getShell() != null)))
{
if (getShell() == null)
{
helpPopup.close();
return;
}
getShell().getDisplay().timerExec(100, new Runnable() {
public void run()
{
if (getShell() != null && !getShell().isDisposed())
{
Rectangle myBounds = getShell().getBounds();
Rectangle helpBounds = new Rectangle(myBounds.x, myBounds.y + myBounds.height, myBounds.width, SWT.DEFAULT);
if (helpPopup != null)
{
if (!create)
{
helpPopup.resize(helpBounds);
return;
}
else
{
// Get ready to create a new pop-up with new help text
helpPopup.close();
}
}
helpPopup = new QueryContextHelp(getShell(), argumentSet.getDescriptor(), helpBounds);
helpPopup.open();
}
}
});
}
}
public void setVisible(boolean f)
{
if (!f)
{
if (applied)
{
applied = false;
// Delay retrieving the VM information until the wizard is displayed
getControl().getDisplay().asyncExec(new Runnable()
{
public void run()
{
acquireDialog.refresh();
}
});
}
if (helpPopup != null)
helpPopup.close();
}
else
{
relocateHelp(true);
}
super.setVisible(f);
}
public void onInputChanged()
{
// A new dump provider configuration has been selected.
// Update next button e.g. if new provider isn't executable.
onError(null);
getContainer().updateButtons();
}
public void onValueChanged()
{
if (!changed)
{
// We do care if the user changes a parameter for finding VMs
// The list of VMs will be refreshed when we leave this page, but clear the
// selection now so the finish button doesn't work.
acquireDialog.clearSelection();
// set the changed flag afterwards so updating the buttons doesn't
// force an applychanges immediately
changed = true;
}
getContainer().updateButtons();
}
public void onError(String message)
{
setErrorMessage(message);
// a work around: calling onFocus ensures a correct update of the error
// message. Without this call it doesn't update the message correct.
onFocus(null);
getContainer().updateButtons();
}
public void onFocus(String message)
{
if (getErrorMessage() != null) setMessage(getErrorMessage(), IMessageProvider.ERROR);
else if (message != null) setMessage(message, IMessageProvider.INFORMATION);
else {
IAnnotatedObjectDescriptor providerDescriptor = table.getProviderDescriptor();
if (providerDescriptor != null) setMessage(providerDescriptor.getName());
else setMessage(null);
}
// Causes recursion from getNextPage();
//getContainer().updateButtons();
}
/**
* Use canFlipToNextPage() instead of {@link #isPageComplete()}
* so that getNextPage() is not called all the time, only when moving.
*/
@Override
public boolean canFlipToNextPage()
{
boolean exec = false;
AnnotatedObjectArgumentsSet argumentSet = table.getArgumentSet();
if (argumentSet != null)
{
// If a provider is selected it should be executable
exec |= argumentSet.isExecutable();
}
else
{
// So long as one provider is executable then we can attempt to get a dump
for (TableItem item : availableProvidersTable.getItems())
{
argumentSet = (AnnotatedObjectArgumentsSet) item.getData();
exec |= argumentSet.isExecutable();
}
}
// If an error message is displayed then don't allow next
return exec && getErrorMessage() == null;
}
@Override
public boolean isPageComplete()
{
return getErrorMessage() == null;
}
@Override
public void performHelp()
{
AnnotatedObjectArgumentsSet currentSet = table.getArgumentSet();
if (currentSet != null)
{
String helpUrl = currentSet.getDescriptor().getHelpUrl();
if (helpUrl != null)
{
PlatformUI.getWorkbench().getHelpSystem().displayHelpResource(helpUrl);
}
}
relocateHelp(true);
PlatformUI.getWorkbench().getHelpSystem().displayHelp("org.eclipse.mat.ui.help.acquire_arguments"); //$NON-NLS-1$
}
/**
* Called from AcquireDialog with a process argument set, use to find the provider
*/
public void processSelected(AnnotatedObjectArgumentsSet argumentSet)
{
if (argumentSet == null)
{
/*
* Hack to force redo of possibly modified provider
* after getting VM list.
*/
AnnotatedObjectArgumentsSet prev = table.getArgumentSet();
table.providerSelected(null);
table.providerSelected(prev);
return;
}
IAnnotatedObjectDescriptor newProviderDescriptor = argumentSet.getDescriptor();
if (newProviderDescriptor instanceof VmInfoDescriptor)
{
IHeapDumpProvider prov = ((VmInfoDescriptor)newProviderDescriptor).getVmInfo().getHeapDumpProvider();
// Walk through table
int index = 0;
for (TableItem item : availableProvidersTable.getItems())
{
AnnotatedObjectArgumentsSet argumentSet2 = (AnnotatedObjectArgumentsSet) item.getData();
IAnnotatedObjectDescriptor desc2 = argumentSet2.getDescriptor();
if (desc2 instanceof HeapDumpProviderDescriptor)
{
if (prov.equals(((HeapDumpProviderDescriptor)desc2).getHeapDumpProvider()))
{
// Set the provider to match the selected process
availableProvidersTable.setSelection(index);
table.providerSelected(argumentSet2);
break;
}
}
++index;
}
}
}
}