package rocks.inspectit.ui.rcp.dialog;
import java.util.Objects;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.expressions.IEvaluationContext;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.PopupDialog;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
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.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.forms.IFormColors;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.widgets.FormText;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.TableWrapData;
import org.eclipse.ui.forms.widgets.TableWrapLayout;
import org.eclipse.ui.handlers.IHandlerService;
import rocks.inspectit.shared.all.exception.RemoteException;
import rocks.inspectit.ui.rcp.InspectIT;
import rocks.inspectit.ui.rcp.InspectITImages;
import rocks.inspectit.ui.rcp.handlers.OpenUrlHandler.ExceptionSupportHandler;
/**
* The dialog for any kind of unexpected messages.
*
* @author Ivan Senic
*
*/
public class ThrowableDialog extends TitleAreaDialog {
/**
* Dialog title.
*/
private static final String DIALOG_TITLE = "Error occurred";
/**
* Main message to show.
*/
private String message;
/**
* Throwable being show.
*/
private Throwable throwable;
/**
* Details composite, where details will be displayed on request.
*/
private Composite detailsComposite;
/**
* {@link FormToolkit}.
*/
private FormToolkit toolkit;
/**
* Default constructor.
*
* @param parentShell
* Parent shell.
* @param message
* Main message to show. If one is not supplied, message from throwable will be
* displayed.
* @param throwable
* Throwable being show.
*/
public ThrowableDialog(Shell parentShell, String message, Throwable throwable) {
super(parentShell);
Assert.isNotNull(throwable, "Throwable to show in the dialog must not be null.");
this.throwable = throwable;
this.message = message;
setShellStyle(getShellStyle() | SWT.RESIZE);
}
/**
* {@inheritDoc}
*/
@Override
public void create() {
super.create();
this.setTitle(DIALOG_TITLE);
this.setTitleImage(InspectIT.getDefault().getImage(InspectITImages.IMG_WIZBAN_ERROR));
}
/**
* {@inheritDoc}
*/
@Override
protected void configureShell(Shell newShell) {
super.configureShell(newShell);
newShell.setText(DIALOG_TITLE);
newShell.addListener(SWT.Resize, new Listener() {
@Override
public void handleEvent(Event event) {
if (areDetailsDisplayed()) {
((GridData) detailsComposite.getLayoutData()).widthHint = getShell().getSize().x;
}
}
});
}
/**
* {@inheritDoc}
*/
@Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.DETAILS_ID, IDialogConstants.SHOW_DETAILS_LABEL, false);
createButton(parent, IDialogConstants.CLIENT_ID, "Send Error Report", false);
createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CLOSE_LABEL, true).setFocus();
}
/**
* {@inheritDoc}
*/
@Override
protected Control createDialogArea(Composite parent) {
toolkit = new FormToolkit(parent.getDisplay());
Composite main = toolkit.createComposite(parent);
GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true);
gd.widthHint = 400;
gd.heightHint = 200;
main.setLayoutData(gd);
TableWrapLayout tableWrapLayout = new TableWrapLayout();
tableWrapLayout.topMargin = 0;
tableWrapLayout.leftMargin = 0;
tableWrapLayout.bottomMargin = 0;
tableWrapLayout.rightMargin = 0;
main.setLayout(tableWrapLayout);
// create separator for better visualization
Composite separator = toolkit.createCompositeSeparator(main);
TableWrapData tableWrapData = new TableWrapData(TableWrapData.FILL_GRAB);
tableWrapData.heightHint = 2;
separator.setLayoutData(tableWrapData);
// then goes content
Composite content = toolkit.createComposite(main);
content.setLayout(new TableWrapLayout());
content.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
// main message
FormText mainFormText = toolkit.createFormText(content, false);
StringBuilder sb = new StringBuilder("<form><p>");
if (throwable instanceof RemoteException) {
String serviceMethod = ((RemoteException) throwable).getServiceMethodSignature();
sb.append("The error occurred while invoking the service method <span color=\"info\">" + serviceMethod + "</span>.");
} else {
sb.append("The unexpected error occurred in the <span color=\"info\">inspectIT User interface</span>.");
}
// message
sb.append("<br/><br/><span color=\"header\" font=\"header\">Error Message</span><br/>");
if (null != message) {
sb.append(message);
} else if (null != throwable.getMessage()) {
sb.append(throwable.getMessage());
} else {
sb.append('-');
}
// error report
sb.append(
"<br/><br/><span color=\"header\" font=\"header\">Send Error Report</span><br/>Please send us the error report to help fix the problem and improve this software.<br/><br/><a href=\"errorReport\">What data does the error report contain?</a></p></form>");
mainFormText.setText(sb.toString(), true, false);
mainFormText.setColor("info", toolkit.getColors().getColor(IFormColors.TITLE));
mainFormText.setColor("header", getShell().getDisplay().getSystemColor(SWT.COLOR_RED));
mainFormText.setFont("header", JFaceResources.getFontRegistry().getBold(JFaceResources.DEFAULT_FONT));
mainFormText.addHyperlinkListener(new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent e) {
showErrorReportDescription();
}
});
return main;
}
/**
* {@inheritDoc}
*/
@Override
protected void buttonPressed(int buttonId) {
super.buttonPressed(buttonId);
if (buttonId == IDialogConstants.DETAILS_ID) {
Composite buttonBar = (Composite) getButtonBar();
Button detailsButton = getButton(buttonId);
// details here
if (areDetailsDisplayed()) {
// if displayed just remove them
detailsComposite.dispose();
detailsButton.setText(IDialogConstants.SHOW_DETAILS_LABEL);
} else {
// if not displayed create complete
createDetails(buttonBar);
detailsButton.setText(IDialogConstants.HIDE_DETAILS_LABEL);
}
fixShellSize();
} else if (buttonId == IDialogConstants.CLIENT_ID) {
try {
IHandlerService handlerService = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class);
ICommandService commandService = (ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class);
Command command = commandService.getCommand(ExceptionSupportHandler.COMMAND);
ExecutionEvent executionEvent = handlerService.createExecutionEvent(command, new Event());
IEvaluationContext context = (IEvaluationContext) executionEvent.getApplicationContext();
context.addVariable(ExceptionSupportHandler.INPUT, throwable);
command.executeWithChecks(executionEvent);
} catch (Exception exception) {
throw new RuntimeException(exception);
}
}
}
/**
* @return If details are currently displayed.
*/
private boolean areDetailsDisplayed() {
return (null != detailsComposite) && !detailsComposite.isDisposed();
}
/**
* manual fixing of shell size when details are shown.
*/
private void fixShellSize() {
int yDelta = getShell().getSize().y - getContents().getSize().y;
((Composite) getContents()).layout(true, true);
Point size = getShell().getSize();
getShell().setSize(size.x, getContents().computeSize(size.x, SWT.DEFAULT).y + yDelta);
}
/**
* Creates details.
*
* @param parent
* Parent composite.
*/
private void createDetails(Composite parent) {
if (!areDetailsDisplayed()) {
detailsComposite = toolkit.createComposite(parent);
TableWrapLayout layout = new TableWrapLayout();
layout.numColumns = 2;
detailsComposite.setLayout(layout);
GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true, ((GridLayout) parent.getLayout()).numColumns, 1);
gd.widthHint = getShell().getSize().x;
detailsComposite.setLayoutData(gd);
Label additonalInfo = toolkit.createLabel(detailsComposite, "Additional Information", SWT.NONE);
additonalInfo.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
additonalInfo.setFont(JFaceResources.getFont(JFaceResources.HEADER_FONT));
TableWrapData twd = new TableWrapData(TableWrapData.FILL_GRAB);
twd.colspan = 2;
additonalInfo.setLayoutData(twd);
Label label = toolkit.createLabel(detailsComposite, "Exception:", SWT.NONE);
label.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
Label exceptionType = toolkit.createLabel(detailsComposite, "", SWT.WRAP);
exceptionType.setLayoutData(new TableWrapData(TableWrapData.FILL));
label = toolkit.createLabel(detailsComposite, "Message:", SWT.NONE);
label.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
Label exceptionMessage = toolkit.createLabel(detailsComposite, "", SWT.WRAP);
exceptionMessage.setLayoutData(new TableWrapData(TableWrapData.FILL));
label = toolkit.createLabel(detailsComposite, "Cause:", SWT.NONE);
label.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
Label exceptionCause = toolkit.createLabel(detailsComposite, "", SWT.WRAP);
exceptionCause.setLayoutData(new TableWrapData(TableWrapData.FILL));
label = toolkit.createLabel(detailsComposite, "Stack trace:", SWT.WRAP);
label.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
Text stackTrace = toolkit.createText(detailsComposite, "", SWT.BORDER | SWT.MULTI | SWT.READ_ONLY | SWT.V_SCROLL | SWT.H_SCROLL);
twd = new TableWrapData(TableWrapData.FILL);
twd.heightHint = 150;
twd.maxWidth = 0;
stackTrace.setLayoutData(twd);
// special for Remote Exception
if (throwable instanceof RemoteException) {
exceptionType.setText(((RemoteException) throwable).getOriginalExceptionClass());
} else {
exceptionType.setText(throwable.getClass().getName());
}
if (null != throwable.getMessage()) {
exceptionMessage.setText(throwable.getMessage());
} else {
exceptionMessage.setText("-");
}
if ((null != throwable.getCause()) && !Objects.equals(throwable, throwable.getCause())) {
exceptionCause.setText(throwable.getCause().getClass().getName() + ": " + throwable.getCause().getMessage());
} else {
exceptionCause.setText("-");
}
stackTrace.setText(ExceptionUtils.getFullStackTrace(throwable));
}
}
/**
* Shows the description box.
*/
private void showErrorReportDescription() {
int shellStyle = SWT.CLOSE | SWT.TITLE | SWT.BORDER | SWT.APPLICATION_MODAL | SWT.RESIZE;
PopupDialog popupDialog = new PopupDialog(getShell(), shellStyle, true, false, false, false, false, "What data does the error report contain?", "What data does the error report contain?") {
private static final int CURSOR_SIZE = 15;
@Override
protected Control createDialogArea(Composite parent) {
Composite composite = (Composite) super.createDialogArea(parent);
Text text = toolkit.createText(parent, null, SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | SWT.H_SCROLL | SWT.V_SCROLL);
GridData gd = new GridData(GridData.BEGINNING | GridData.FILL_BOTH);
gd.horizontalIndent = 3;
gd.verticalIndent = 3;
text.setLayoutData(gd);
text.setText(
"The error report to send to the inspectIT Team contains the following information:\n\n - inspectIT version you are using\n - Your operating system information and used Java version\n - Exception type, message and stack trace\n\nThe sending of the error report is only available via Send-To mail option. After clicking to the 'Send Error Report' button your e-mail client will open with new predefined e-mail message to send to support.inspectit@novatec-gmbh.de. The e-mail used to send the error report will be kept private.\n\nWe thank you in advance for helping us to fix this problem and improve our software.");
return composite;
}
@Override
protected Point getInitialLocation(Point initialSize) {
// show popup relative to cursor
Display display = getShell().getDisplay();
Point location = display.getCursorLocation();
location.x += CURSOR_SIZE;
location.y += CURSOR_SIZE;
return location;
}
@Override
protected Point getInitialSize() {
return new Point(600, 300);
}
};
popupDialog.open();
}
}