blob: c2914978f44819303ae2f23f5e76e05d85bebf8b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.internal.ide;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWTError;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.application.IWorkbenchConfigurer;
import org.eclipse.ui.internal.ide.dialogs.InternalErrorDialog;
import org.eclipse.ui.progress.IProgressConstants;
import org.eclipse.ui.progress.UIJob;
import org.eclipse.ui.statushandlers.StatusAdapter;
import org.eclipse.ui.statushandlers.StatusManager;
import org.eclipse.ui.statushandlers.WorkbenchErrorHandler;
import com.ibm.icu.text.MessageFormat;
/**
* This is the IDE workbench error handler. The instance of this handler is
* returned from IDEWorkbenchAdvisor#getWorkbenchErrorHandler(). All handled
* statuses are checked against severity and logged using logging facility (by
* superclass).
*
* @since 3.3
*/
public class IDEWorkbenchErrorHandler extends WorkbenchErrorHandler {
private int exceptionCount = 0;
static private FatalErrorDialog dialog;
private boolean closing = false;
private IWorkbenchConfigurer workbenchConfigurer;
// Pre-load all Strings trying to run as light as possible in case of fatal
// errors.
private static String MSG_OutOfMemoryError = IDEWorkbenchMessages.FatalError_OutOfMemoryError;
private static String MSG_StackOverflowError = IDEWorkbenchMessages.FatalError_StackOverflowError;
private static String MSG_VirtualMachineError = IDEWorkbenchMessages.FatalError_VirtualMachineError;
private static String MSG_SWTError = IDEWorkbenchMessages.FatalError_SWTError;
private static String MSG_FATAL_ERROR = IDEWorkbenchMessages.FatalError;
private static String MSG_FATAL_ERROR_Recursive = IDEWorkbenchMessages.FatalError_RecursiveError;
private static String MSG_FATAL_ERROR_Title = IDEWorkbenchMessages.InternalError;
// cache handled statuses
private final Map map = Collections.synchronizedMap(new WeakHashMap());
/**
* @param configurer
*/
public IDEWorkbenchErrorHandler(IWorkbenchConfigurer configurer) {
workbenchConfigurer = configurer;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.statushandlers.WorkbenchErrorHandler#handle(org.eclipse.ui.statushandlers.StatusAdapter,
* int)
*/
public void handle(final StatusAdapter statusAdapter, int style) {
// if fatal error occurs, we will show the blocking error dialog anyway
if (isFatal(statusAdapter)) {
// if we modify the hint, we have to be sure that status picked up
// from .log will not be handled if it is reported independently via
// StatusManager
if (!map.containsKey(statusAdapter.getStatus())) {
map.put(statusAdapter.getStatus(), null);
} else {
return;
}
if (statusAdapter
.getProperty(IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY) == Boolean.TRUE) {
statusAdapter.setProperty(
IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY,
Boolean.FALSE);
}
super.handle(statusAdapter, style | StatusManager.BLOCK);
} else {
super.handle(statusAdapter, style);
}
// if fatal error occurs, we will ask to close the workbench
if (isFatal(statusAdapter)) {
UIJob handlingExceptionJob = new UIJob("IDE Exception Handler") //$NON-NLS-1$
{
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
*/
public IStatus runInUIThread(IProgressMonitor monitor) {
handleException(statusAdapter.getStatus().getException());
return new Status(
IStatus.OK,
IDEWorkbenchPlugin.IDE_WORKBENCH,
IDEWorkbenchMessages.IDEExceptionHandler_ExceptionHandledMessage);
}
};
handlingExceptionJob.setSystem(true);
handlingExceptionJob.schedule();
}
}
private boolean isFatal(final StatusAdapter statusAdapter) {
if (statusAdapter.getStatus().getException() != null
&& (statusAdapter.getStatus().getException() instanceof OutOfMemoryError
|| statusAdapter.getStatus().getException() instanceof StackOverflowError
|| statusAdapter.getStatus().getException() instanceof VirtualMachineError || statusAdapter
.getStatus().getException() instanceof SWTError)) {
return true;
}
return false;
}
private void handleException(Throwable t) {
try {
exceptionCount++;
if (exceptionCount > 1) {
dialog.updateMessage(MessageFormat.format(MSG_FATAL_ERROR,
new Object[] { MSG_FATAL_ERROR_Recursive }));
dialog.getShell().forceActive();
} else {
if (openQuestionDialog(t)) {
closeWorkbench();
}
}
} finally {
exceptionCount--;
}
}
/**
* Informs the user about a fatal error. Returns true if the user decide to
* exit workbench or if another fatal error happens while reporting it.
*/
private boolean openQuestionDialog(Throwable t) {
try {
String msg = null;
if (t instanceof OutOfMemoryError) {
msg = MSG_OutOfMemoryError;
} else if (t instanceof StackOverflowError) {
msg = MSG_StackOverflowError;
} else if (t instanceof VirtualMachineError) {
msg = MSG_VirtualMachineError;
} else if (t instanceof SWTError) {
msg = MSG_SWTError;
} else {
if (t.getMessage() == null) {
msg = IDEWorkbenchMessages.InternalErrorNoArg;
} else {
msg = NLS.bind(IDEWorkbenchMessages.InternalErrorOneArg, t
.getMessage());
}
}
// Always open the dialog in case of major error but do not show the
// detail button if not in debug mode.
Throwable detail = t;
if (!Policy.DEBUG_OPEN_ERROR_DIALOG) {
detail = null;
}
dialog = openInternalQuestionDialog(PlatformUI.getWorkbench()
.getActiveWorkbenchWindow().getShell(),
MSG_FATAL_ERROR_Title, MessageFormat.format(
MSG_FATAL_ERROR, new Object[] { msg }), detail, 1);
return dialog.open() == 0;
} catch (Throwable th) {
// Workbench may be in such bad shape (no OS handles left, out of
// memory, etc)
// that is cannot show a message to the user. Just bail out now.
System.err
.println("Error while informing user about event loop exception:"); //$NON-NLS-1$
t.printStackTrace();
System.err.println("Dialog open exception:"); //$NON-NLS-1$
th.printStackTrace();
return true;
}
}
private FatalErrorDialog openInternalQuestionDialog(Shell parent,
String title, String message, Throwable detail, int defaultIndex) {
String[] labels;
if (detail == null) {
labels = new String[] { IDialogConstants.YES_LABEL,
IDialogConstants.NO_LABEL };
} else {
labels = new String[] { IDialogConstants.YES_LABEL,
IDialogConstants.NO_LABEL,
IDialogConstants.SHOW_DETAILS_LABEL };
}
FatalErrorDialog dialog = new FatalErrorDialog(parent, title, null, // accept
// the
// default
// window
// icon
message, detail, MessageDialog.QUESTION, labels, defaultIndex);
if (detail != null) {
dialog.setDetailButton(2);
}
return dialog;
}
/**
* Closes the workbench and make sure all exceptions are handled.
*/
private void closeWorkbench() {
if (closing) {
return;
}
try {
closing = true;
if (dialog != null && dialog.getShell() != null
&& !dialog.getShell().isDisposed()) {
dialog.close();
}
//@see WorkbenchAdvisor#getWorkbenchConfigurer()
if (workbenchConfigurer != null)
workbenchConfigurer.emergencyClose();
} catch (RuntimeException re) {
// Workbench may be in such bad shape (no OS handles left, out of
// memory, etc)
// that is cannot even close. Just bail out now.
System.err
.println("Fatal runtime error happened during workbench emergency close."); //$NON-NLS-1$
re.printStackTrace();
throw re;
} catch (Error e) {
// Workbench may be in such bad shape (no OS handles left, out of
// memory, etc)
// that is cannot even close. Just bail out now.
System.err
.println("Fatal error happened during workbench emergency close."); //$NON-NLS-1$
e.printStackTrace();
throw e;
}
}
private class FatalErrorDialog extends InternalErrorDialog {
/**
* @param parentShell
* @param dialogTitle
* @param dialogTitleImage
* @param dialogMessage
* @param detail
* @param dialogImageType
* @param dialogButtonLabels
* @param defaultIndex
*/
public FatalErrorDialog(Shell parentShell, String dialogTitle,
Image dialogTitleImage, String dialogMessage, Throwable detail,
int dialogImageType, String[] dialogButtonLabels,
int defaultIndex) {
super(parentShell, dialogTitle, dialogTitleImage, dialogMessage,
detail, dialogImageType, dialogButtonLabels, defaultIndex);
}
/**
* Updates the dialog message
*
* @param message
* new message
*/
public void updateMessage(String message) {
this.message = message;
this.messageLabel.setText(message);
this.messageLabel.update();
}
}
}