| /******************************************************************************* |
| * Copyright (c) 2006, 2007 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 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 {@link 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; |
| |
| /** |
| * @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 (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) && style != StatusManager.NONE) { |
| 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(); |
| } |
| 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(); |
| } |
| } |
| } |