001: /*******************************************************************************
002: * Copyright (c) 2006, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.ide;
011:
012: import org.eclipse.core.runtime.IProgressMonitor;
013: import org.eclipse.core.runtime.IStatus;
014: import org.eclipse.core.runtime.Status;
015: import org.eclipse.jface.dialogs.IDialogConstants;
016: import org.eclipse.jface.dialogs.MessageDialog;
017: import org.eclipse.osgi.util.NLS;
018: import org.eclipse.swt.SWTError;
019: import org.eclipse.swt.graphics.Image;
020: import org.eclipse.swt.widgets.Shell;
021: import org.eclipse.ui.PlatformUI;
022: import org.eclipse.ui.application.IWorkbenchConfigurer;
023: import org.eclipse.ui.internal.ide.dialogs.InternalErrorDialog;
024: import org.eclipse.ui.progress.IProgressConstants;
025: import org.eclipse.ui.progress.UIJob;
026: import org.eclipse.ui.statushandlers.StatusAdapter;
027: import org.eclipse.ui.statushandlers.StatusManager;
028: import org.eclipse.ui.statushandlers.WorkbenchErrorHandler;
029:
030: import com.ibm.icu.text.MessageFormat;
031:
032: /**
033: * This is the IDE workbench error handler. The instance of this handler is
034: * returned from {@link IDEWorkbenchAdvisor#getWorkbenchErrorHandler()}. All
035: * handled statuses are checked against severity and logged using logging
036: * facility (by superclass).
037: *
038: * @since 3.3
039: */
040: public class IDEWorkbenchErrorHandler extends WorkbenchErrorHandler {
041:
042: private int exceptionCount = 0;
043:
044: static private FatalErrorDialog dialog;
045:
046: private boolean closing = false;
047:
048: private IWorkbenchConfigurer workbenchConfigurer;
049:
050: // Pre-load all Strings trying to run as light as possible in case of fatal
051: // errors.
052: private static String MSG_OutOfMemoryError = IDEWorkbenchMessages.FatalError_OutOfMemoryError;
053:
054: private static String MSG_StackOverflowError = IDEWorkbenchMessages.FatalError_StackOverflowError;
055:
056: private static String MSG_VirtualMachineError = IDEWorkbenchMessages.FatalError_VirtualMachineError;
057:
058: private static String MSG_SWTError = IDEWorkbenchMessages.FatalError_SWTError;
059:
060: private static String MSG_FATAL_ERROR = IDEWorkbenchMessages.FatalError;
061:
062: private static String MSG_FATAL_ERROR_Recursive = IDEWorkbenchMessages.FatalError_RecursiveError;
063:
064: private static String MSG_FATAL_ERROR_Title = IDEWorkbenchMessages.InternalError;
065:
066: /**
067: * @param configurer
068: */
069: public IDEWorkbenchErrorHandler(IWorkbenchConfigurer configurer) {
070: workbenchConfigurer = configurer;
071: }
072:
073: /*
074: * (non-Javadoc)
075: *
076: * @see org.eclipse.ui.statushandlers.WorkbenchErrorHandler#handle(org.eclipse.ui.statushandlers.StatusAdapter,
077: * int)
078: */
079: public void handle(final StatusAdapter statusAdapter, int style) {
080:
081: // if fatal error occurs, we will show the blocking error dialog anyway
082: if (isFatal(statusAdapter)) {
083: if (statusAdapter
084: .getProperty(IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY) == Boolean.TRUE) {
085: statusAdapter
086: .setProperty(
087: IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY,
088: Boolean.FALSE);
089: }
090: super .handle(statusAdapter, style | StatusManager.BLOCK);
091: } else {
092: super .handle(statusAdapter, style);
093: }
094:
095: // if fatal error occurs, we will ask to close the workbench
096: if (isFatal(statusAdapter) && style != StatusManager.NONE) {
097: UIJob handlingExceptionJob = new UIJob(
098: "IDE Exception Handler") //$NON-NLS-1$
099: {
100: /*
101: * (non-Javadoc)
102: *
103: * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
104: */
105: public IStatus runInUIThread(IProgressMonitor monitor) {
106: handleException(statusAdapter.getStatus()
107: .getException());
108: return new Status(
109: IStatus.OK,
110: IDEWorkbenchPlugin.IDE_WORKBENCH,
111: IDEWorkbenchMessages.IDEExceptionHandler_ExceptionHandledMessage);
112: }
113:
114: };
115:
116: handlingExceptionJob.setSystem(true);
117: handlingExceptionJob.schedule();
118: }
119: }
120:
121: private boolean isFatal(final StatusAdapter statusAdapter) {
122: if (statusAdapter.getStatus().getException() != null
123: && (statusAdapter.getStatus().getException() instanceof OutOfMemoryError
124: || statusAdapter.getStatus().getException() instanceof StackOverflowError
125: || statusAdapter.getStatus().getException() instanceof VirtualMachineError || statusAdapter
126: .getStatus().getException() instanceof SWTError)) {
127: return true;
128: }
129: return false;
130: }
131:
132: private void handleException(Throwable t) {
133: try {
134: exceptionCount++;
135: if (exceptionCount > 1) {
136: dialog.updateMessage(MessageFormat.format(
137: MSG_FATAL_ERROR,
138: new Object[] { MSG_FATAL_ERROR_Recursive }));
139: dialog.getShell().forceActive();
140: } else {
141: if (openQuestionDialog(t)) {
142: closeWorkbench();
143: }
144: }
145: } finally {
146: exceptionCount--;
147: }
148: }
149:
150: /**
151: * Informs the user about a fatal error. Returns true if the user decide to
152: * exit workbench or if another fatal error happens while reporting it.
153: */
154: private boolean openQuestionDialog(Throwable t) {
155: try {
156: String msg = null;
157: if (t instanceof OutOfMemoryError) {
158: msg = MSG_OutOfMemoryError;
159: } else if (t instanceof StackOverflowError) {
160: msg = MSG_StackOverflowError;
161: } else if (t instanceof VirtualMachineError) {
162: msg = MSG_VirtualMachineError;
163: } else if (t instanceof SWTError) {
164: msg = MSG_SWTError;
165: } else {
166: if (t.getMessage() == null) {
167: msg = IDEWorkbenchMessages.InternalErrorNoArg;
168: } else {
169: msg = NLS.bind(
170: IDEWorkbenchMessages.InternalErrorOneArg, t
171: .getMessage());
172: }
173: }
174:
175: // Always open the dialog in case of major error but do not show the
176: // detail button if not in debug mode.
177: Throwable detail = t;
178: if (!Policy.DEBUG_OPEN_ERROR_DIALOG) {
179: detail = null;
180: }
181:
182: dialog = openInternalQuestionDialog(PlatformUI
183: .getWorkbench().getActiveWorkbenchWindow()
184: .getShell(), MSG_FATAL_ERROR_Title, MessageFormat
185: .format(MSG_FATAL_ERROR, new Object[] { msg }),
186: detail, 1);
187:
188: return dialog.open() == 0;
189: } catch (Throwable th) {
190: // Workbench may be in such bad shape (no OS handles left, out of
191: // memory, etc)
192: // that is cannot show a message to the user. Just bail out now.
193: System.err
194: .println("Error while informing user about event loop exception:"); //$NON-NLS-1$
195: t.printStackTrace();
196: System.err.println("Dialog open exception:"); //$NON-NLS-1$
197: th.printStackTrace();
198: return true;
199: }
200: }
201:
202: private FatalErrorDialog openInternalQuestionDialog(Shell parent,
203: String title, String message, Throwable detail,
204: int defaultIndex) {
205: String[] labels;
206: if (detail == null) {
207: labels = new String[] { IDialogConstants.YES_LABEL,
208: IDialogConstants.NO_LABEL };
209: } else {
210: labels = new String[] { IDialogConstants.YES_LABEL,
211: IDialogConstants.NO_LABEL,
212: IDialogConstants.SHOW_DETAILS_LABEL };
213: }
214:
215: FatalErrorDialog dialog = new FatalErrorDialog(
216: parent,
217: title,
218: null, // accept
219: // the
220: // default
221: // window
222: // icon
223: message, detail, MessageDialog.QUESTION, labels,
224: defaultIndex);
225: if (detail != null) {
226: dialog.setDetailButton(2);
227: }
228: return dialog;
229: }
230:
231: /**
232: * Closes the workbench and make sure all exceptions are handled.
233: */
234: private void closeWorkbench() {
235: if (closing) {
236: return;
237: }
238:
239: try {
240: closing = true;
241: if (dialog != null && dialog.getShell() != null
242: && !dialog.getShell().isDisposed()) {
243: dialog.close();
244: }
245: workbenchConfigurer.emergencyClose();
246: } catch (RuntimeException re) {
247: // Workbench may be in such bad shape (no OS handles left, out of
248: // memory, etc)
249: // that is cannot even close. Just bail out now.
250: System.err
251: .println("Fatal runtime error happened during workbench emergency close."); //$NON-NLS-1$
252: re.printStackTrace();
253: throw re;
254: } catch (Error e) {
255: // Workbench may be in such bad shape (no OS handles left, out of
256: // memory, etc)
257: // that is cannot even close. Just bail out now.
258: System.err
259: .println("Fatal error happened during workbench emergency close."); //$NON-NLS-1$
260: e.printStackTrace();
261: throw e;
262: }
263: }
264:
265: private class FatalErrorDialog extends InternalErrorDialog {
266:
267: /**
268: * @param parentShell
269: * @param dialogTitle
270: * @param dialogTitleImage
271: * @param dialogMessage
272: * @param detail
273: * @param dialogImageType
274: * @param dialogButtonLabels
275: * @param defaultIndex
276: */
277: public FatalErrorDialog(Shell parentShell, String dialogTitle,
278: Image dialogTitleImage, String dialogMessage,
279: Throwable detail, int dialogImageType,
280: String[] dialogButtonLabels, int defaultIndex) {
281: super (parentShell, dialogTitle, dialogTitleImage,
282: dialogMessage, detail, dialogImageType,
283: dialogButtonLabels, defaultIndex);
284: }
285:
286: /**
287: * Updates the dialog message
288: *
289: * @param message
290: * new message
291: */
292: public void updateMessage(String message) {
293: this.message = message;
294: this.messageLabel.setText(message);
295: this.messageLabel.update();
296: }
297: }
298: }
|