package rocks.inspectit.ui.rcp.ci.dialog;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
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.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import rocks.inspectit.shared.all.cmr.model.PlatformIdent;
import rocks.inspectit.shared.all.communication.data.cmr.AgentStatusData;
import rocks.inspectit.ui.rcp.InspectIT;
import rocks.inspectit.ui.rcp.InspectITImages;
import rocks.inspectit.ui.rcp.formatter.ImageFormatter;
import rocks.inspectit.ui.rcp.preferences.PreferencesConstants;
import rocks.inspectit.ui.rcp.preferences.PreferencesUtils;
/**
* This class represents the dialog to choose which agents' instrumentation should be updated.
*
* @author Marius Oehler
*
*/
public class InstrumentationUpdateDialog extends TitleAreaDialog {
/**
* Behavior when saving a changed instrumentation and agents are affected of the change.
*/
public enum OnSaveBehavior {
/**
* Show the dialog to the user.
*/
SHOW_DIALOG,
/**
* Show no dialog and update all agents to the latest instrumentation.
*/
UPDATE_ALL_AGENTS,
/**
* Show no dialog and do not update any agent.
*/
DO_NOTHING;
}
/**
* Default message used when no title is defined.
*/
private static final String DEFAULT_MESSAGE = "The following agents have pending instrumentations. Please select the agents which instrumentation should be updated.";
/**
* Default title used when no message is defined.
*/
private static final String DEFAULT_TITLE = "Updating Agent Instrumentation";
/**
* The table showing pending agents.
*/
private Table table;
/**
* Button to select all agents.
*/
private Button buttonSelectAllAgents;
/**
* Button to select specific agents.
*/
private Button buttonSelectSpecificAgents;
/**
* Label of the {@link #comboAutoAction}.
*/
private Label labelAlwaysDo;
/**
* Combo to select the default on-save action.
*/
private Combo comboAutoAction;
/**
* Button to always show this dialog when a agent is pending after instrumentation change.
*/
private Button buttonAlwaysShowDialog;
/**
* Map of agents which instrumentation is in a pending state.
*/
private Map<PlatformIdent, AgentStatusData> pendingAgents;
/**
* The agents which were selected by the user to update.
*/
private Collection<PlatformIdent> selectedAgents;
/**
* The text on the close button.
*/
private String closeButtonLabel;
/**
* The selected agent.
*/
private final PlatformIdent platformIdent;
/**
* The update button.
*/
private Button updateButton;
/**
* Constructor.
*
* @param parentShell
* the parent shell
* @param pendingAgents
* {@link Map} of agents which instrumentation is not up-to-date
*/
public InstrumentationUpdateDialog(Shell parentShell, Map<PlatformIdent, AgentStatusData> pendingAgents) {
this(parentShell, pendingAgents, null, null);
}
/**
* Constructor.
*
* @param parentShell
* the parent shell
* @param pendingAgents
* {@link Map} of agents which instrumentation is not up-to-date
* @param platformIdent
* the selected agent
* @param closeButtonLabel
* label of the closing button
*/
public InstrumentationUpdateDialog(Shell parentShell, Map<PlatformIdent, AgentStatusData> pendingAgents, PlatformIdent platformIdent, String closeButtonLabel) {
super(parentShell);
this.pendingAgents = pendingAgents;
this.platformIdent = platformIdent;
if (closeButtonLabel == null) {
this.closeButtonLabel = IDialogConstants.CLOSE_LABEL;
} else {
this.closeButtonLabel = closeButtonLabel;
}
setShellStyle(getShellStyle() | SWT.RESIZE);
}
/**
* {@inheritDoc}
*/
@Override
public void create() {
super.create();
this.setTitle(DEFAULT_TITLE);
this.setMessage(DEFAULT_MESSAGE, IMessageProvider.INFORMATION);
}
/**
* {@inheritDoc}
*/
@Override
protected void configureShell(Shell newShell) {
super.configureShell(newShell);
newShell.setText(DEFAULT_TITLE);
newShell.setMinimumSize(525, 300);
}
/**
* {@inheritDoc}
*/
@Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.CANCEL_ID, closeButtonLabel, false);
updateButton = createButton(parent, IDialogConstants.OK_ID, "Update", true);
if (MapUtils.isEmpty(pendingAgents)) {
updateButton.setEnabled(false);
}
}
/**
* {@inheritDoc}
*/
@Override
protected Control createDialogArea(Composite parent) {
boolean selectAllAgents = platformIdent == null;
Composite main = new Composite(parent, SWT.NONE);
main.setLayout(new GridLayout(1, false));
main.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
// creating checkboxes
buttonSelectAllAgents = new Button(main, SWT.RADIO);
buttonSelectAllAgents.setText("All agent(s)");
buttonSelectAllAgents.setSelection(selectAllAgents);
buttonSelectAllAgents.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
table.setEnabled(false);
}
});
buttonSelectSpecificAgents = new Button(main, SWT.RADIO);
buttonSelectSpecificAgents.setText("Select specific Agent(s)");
buttonSelectSpecificAgents.setSelection(!selectAllAgents);
buttonSelectSpecificAgents.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
table.setEnabled(true);
}
});
// table for agents
table = new Table(main, SWT.CHECK | SWT.V_SCROLL | SWT.BORDER | SWT.FULL_SELECTION);
GridData tableLayout = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1);
tableLayout.heightHint = 150;
table.setLayoutData(tableLayout);
table.setLinesVisible(false);
table.setEnabled(!selectAllAgents);
// fill the table
updateTableContent();
Label horizontalSeparator = new Label(main, SWT.SEPARATOR | SWT.HORIZONTAL);
horizontalSeparator.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
// lower part to select default action
buttonAlwaysShowDialog = new Button(main, SWT.CHECK);
buttonAlwaysShowDialog.setText("Ask me on each instrumentation update");
buttonAlwaysShowDialog.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
comboAutoAction.setEnabled(!((Button) e.getSource()).getSelection());
labelAlwaysDo.setEnabled(!((Button) e.getSource()).getSelection());
}
});
Composite composite = new Composite(main, SWT.NONE);
composite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
composite.setLayout(new GridLayout(2, false));
labelAlwaysDo = new Label(composite, SWT.NONE);
labelAlwaysDo.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
labelAlwaysDo.setText("Always do:");
comboAutoAction = new Combo(composite, SWT.DROP_DOWN | SWT.READ_ONLY);
comboAutoAction.setItems(new String[] { "Update all affected agents", "Do not update any agent" });
comboAutoAction.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
comboAutoAction.setEnabled(false);
Composite compositeInfo = new Composite(main, SWT.NONE);
compositeInfo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
compositeInfo.setLayout(new GridLayout(2, false));
Label infoIconLabel = new Label(compositeInfo, SWT.NONE);
infoIconLabel.setImage(InspectIT.getDefault().getImage(InspectITImages.IMG_WARNING));
Label infoTextLabel = new Label(compositeInfo, SWT.NONE);
infoTextLabel.setText("It can take up to 30 seconds to apply the instrumentation changes.");
// select the combo and checkbox to the stored state
InstrumentationUpdateDialog.OnSaveBehavior saveBehavior = PreferencesUtils.getObject(PreferencesConstants.INSTRUMENTATION_UPDATED_AUTO_ACTION);
boolean alwaysShowDialog = saveBehavior == OnSaveBehavior.SHOW_DIALOG;
int selectedIndex = saveBehavior == OnSaveBehavior.DO_NOTHING ? 1 : 0;
buttonAlwaysShowDialog.setSelection(alwaysShowDialog);
labelAlwaysDo.setEnabled(!alwaysShowDialog);
comboAutoAction.setEnabled(!alwaysShowDialog);
comboAutoAction.select(selectedIndex);
applyDialogFont(main);
return main;
}
/**
* Updates the content of the table. Creates an entry for each agent contained in the
* {@link #pendingAgents} map.
*/
private void updateTableContent() {
table.clearAll();
SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy hh:mm aa");
Map<PlatformIdent, AgentStatusData> sortedPendingAgents = sortPendingAgentsByAgentName(pendingAgents);
for (Entry<PlatformIdent, AgentStatusData> entry : sortedPendingAgents.entrySet()) {
TableItem tableItem = new TableItem(table, SWT.NONE);
tableItem.setData(entry.getKey());
tableItem.setText(getAgentText(entry, dateFormat));
tableItem.setImage(ImageFormatter.getAgentImage(entry.getValue()));
if ((platformIdent != null) && entry.getKey().equals(platformIdent)) {
tableItem.setChecked(true);
}
}
}
/**
* Returns a descriptive text for the given agent. It contains a date which is formated using
* the given {@link SimpleDateFormat}.
*
* @param entry
* the agent
* @param format
* used {@link SimpleDateFormat} to format the date
* @return descriptive {@link String} of the given agent
*/
private String getAgentText(Entry<PlatformIdent, AgentStatusData> entry, SimpleDateFormat format) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(entry.getKey().getAgentName());
stringBuffer.append(" [");
stringBuffer.append(entry.getKey().getVersion());
stringBuffer.append("] - Pending since: ");
stringBuffer.append(format.format(new Date(entry.getValue().getLastInstrumentationUpate())));
return stringBuffer.toString();
}
/**
* Sorts the given map of platform idents on the agent's name.
*
* @param map
* {@link Map} containing platform idents
* @return a sorted representation of the given {@link Map}
*/
private static Map<PlatformIdent, AgentStatusData> sortPendingAgentsByAgentName(Map<PlatformIdent, AgentStatusData> map) {
List<Map.Entry<PlatformIdent, AgentStatusData>> list = new LinkedList<>(map.entrySet());
Collections.sort(list, new Comparator<Map.Entry<PlatformIdent, AgentStatusData>>() {
@Override
public int compare(Map.Entry<PlatformIdent, AgentStatusData> left, Map.Entry<PlatformIdent, AgentStatusData> right) {
String nameLeft = left.getKey().getAgentName();
String nameRight = right.getKey().getAgentName();
return nameLeft.compareTo(nameRight);
}
});
Map<PlatformIdent, AgentStatusData> result = new LinkedHashMap<>();
for (Map.Entry<PlatformIdent, AgentStatusData> entry : list) {
result.put(entry.getKey(), entry.getValue());
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
protected void okPressed() {
persistOnSaveBehavior();
// save selected agents
if (buttonSelectAllAgents.getSelection()) {
selectedAgents = new ArrayList<>(pendingAgents.keySet());
} else {
selectedAgents = new ArrayList<>();
for (TableItem item : table.getItems()) {
if (item.getChecked()) {
selectedAgents.add((PlatformIdent) item.getData());
}
}
}
super.okPressed();
}
/**
* {@inheritDoc}
*/
@Override
protected void cancelPressed() {
persistOnSaveBehavior();
super.cancelPressed();
}
/**
* Persists the currently selected "on save behavior" for the instrumentation update.
*/
private void persistOnSaveBehavior() {
boolean askMe = buttonAlwaysShowDialog.getSelection();
if (askMe) {
PreferencesUtils.saveObject(PreferencesConstants.INSTRUMENTATION_UPDATED_AUTO_ACTION, OnSaveBehavior.SHOW_DIALOG, false);
} else {
if (comboAutoAction.getSelectionIndex() == 0) {
PreferencesUtils.saveObject(PreferencesConstants.INSTRUMENTATION_UPDATED_AUTO_ACTION, OnSaveBehavior.UPDATE_ALL_AGENTS, false);
} else {
PreferencesUtils.saveObject(PreferencesConstants.INSTRUMENTATION_UPDATED_AUTO_ACTION, OnSaveBehavior.DO_NOTHING, false);
}
}
}
/**
* Gets {@link #selectedAgents}.
*
* @return {@link #selectedAgents}
*/
@SuppressWarnings("unchecked")
public Collection<PlatformIdent> getUpdateAgents() {
if (CollectionUtils.isEmpty(selectedAgents)) {
return Collections.EMPTY_LIST;
}
return this.selectedAgents;
}
}