1
/*******************************************************************************
2
* Copyright (c) 2006, 2008 Wind River Systems and others.
3
* All rights reserved. This program and the accompanying materials
4
* are made available under the terms of the Eclipse Public License v1.0
5
* which accompanies this distribution, and is available at
6
* http://www.eclipse.org/legal/epl-v10.html
9
* Wind River Systems - initial API and implementation
10
*******************************************************************************/
11
package org.eclipse.cdt.dsf.concurrent;
13
import java.util.concurrent.Executor;
14
import java.util.concurrent.RejectedExecutionException;
16
import org.eclipse.cdt.dsf.internal.DsfPlugin;
17
import org.eclipse.core.runtime.IStatus;
18
import org.eclipse.core.runtime.ListenerList;
19
import org.eclipse.core.runtime.MultiStatus;
20
import org.eclipse.core.runtime.Status;
23
* Used to monitor the result of an asynchronous request. Because of the
24
* asynchronous nature of DSF code, a very large number of methods needs to
25
* signal the result of an operation through a call-back. This class is the base
26
* class for such call backs.
28
* The intended use of this class, is that a client who is calling an asynchronous
29
* method, will sub-class RequestMonitor, and implement the method {@link #handleCompleted()},
30
* or any of the other <code>handle...</code> methods, in order to interpret the
31
* results of the request. The object implementing the asynchronous method is required
32
* to call the {@link #done()} method on the request monitor object that it received
36
* The severity of the {@link IStatus> returned by #getStatus() can be used to
37
* determine the success or failure of the asynchronous operation. By convention
38
* the error codes returned by asynchronous method should be interpreted as follows:
40
* <li>OK and INFO - Result is a success. In DataRequestMonitor, getData() should
41
* return a value.</li>
42
* <li>WARNING - Acceptable error condition (getData() may return null). Where for
43
* example user tried to retrieve variable data, but the program resumed in the
44
* mean time and an event will be generated shortly which will clear the variables
46
* <li>ERROR - An error condition that should probably be reported to the user.</li>
47
* <li>CANCEL - The request was canceled, and the asynchronous method was not
52
* The RequestMonitor constructor accepts an optional "parent" request monitor. If a
53
* parent monitor is specified, it will automatically be invoked by this monitor when
54
* the request is completed. The parent option is useful when implementing a method
55
* which is asynchronous (and accepts a request monitor as an argument) and which itself
56
* calls another asynchronous method to complete its operation. For example, in the
57
* request monitor implementation below, the implementation only needs to override
58
* <code>handleOK()</code>, because the base implementation will handle notifying the
59
* parent <code>rm</code> in case the <code>getIngredients()</code> call fails.
61
* public void createCupCakes(final DataRequestMonitor<CupCake[]> rm) {
62
* getIngredients(new DataRequestMonitor<Ingredients>(fExecutor, rm) {
63
* public void handleOK() {
64
* rm.setData( new CupCake(getData().getFlour(), getData().getSugar(),
65
* getData().getBakingPowder()));
76
public class RequestMonitor extends DsfExecutable {
79
* Interface used by RequestMonitor to notify when a given request monitor
84
public static interface ICanceledListener {
87
* Called when the given request monitor is canceled.
89
public void requestCanceled(RequestMonitor rm);
93
* The executor that will be used in order to invoke the handler of the results
96
private final Executor fExecutor;
99
* The request monitor which was used to call into the method that created this
102
private final RequestMonitor fParentRequestMonitor;
104
private ListenerList fCancelListeners;
109
private IStatus fStatus = Status.OK_STATUS;
110
private boolean fCanceled = false;
111
private boolean fDone = false;
114
* Constructor with an optional parent monitor.
115
* @param executor This executor will be used to invoke the runnable that
116
* will allow processing the completion code of this request monitor.
117
* @param parentRequestMonitor The optional parent request monitor to be invoked by
118
* default when this request completes. Parameter may be null.
120
public RequestMonitor(Executor executor, RequestMonitor parentRequestMonitor) {
121
fExecutor = executor;
122
fParentRequestMonitor = parentRequestMonitor;
124
// If the parent rm is not null, add ourselves as a listener so that
125
// this request monitor will automatically be canceled when the parent
127
if (fParentRequestMonitor != null) {
128
fParentRequestMonitor.addCancelListener(
129
new ICanceledListener() {
130
public void requestCanceled(RequestMonitor rm) {
138
* Sets the status of the result of the request. If status is OK, this
139
* method does not need to be called.
141
public synchronized void setStatus(IStatus status) {
142
assert isCanceled() || status.getSeverity() != IStatus.CANCEL;
146
/** Returns the status of the completed method. */
147
public synchronized IStatus getStatus() {
149
return Status.CANCEL_STATUS;
155
* Sets this request monitor as canceled and calls the cancel listeners if any.
157
* Note: Calling cancel() does not automatically complete the RequestMonitor.
158
* The asynchronous call still has to call done().
161
* Note: logically a request should only be canceled by the client that issued
162
* the request in the first place. After a request is canceled, the method
163
* that is fulfilling the request may call {@link #setStatus(IStatus)} with
164
* severity of <code>IStatus.CANCEL</code> to indicate that it recognized that
165
* the given request was canceled and it did not perform the given operation.
168
public void cancel() {
169
Object[] listeners = null;
170
synchronized (this) {
171
// Check to make sure the request monitor wasn't previously canceled.
174
if (fCancelListeners != null) {
175
listeners = fCancelListeners.getListeners();
180
// Call the listeners outside of a synchronized section to reduce the
181
// risk of deadlocks.
182
if (listeners != null) {
183
for (Object listener : listeners) {
184
((ICanceledListener)listener).requestCanceled(this);
190
* Returns whether the request was canceled. Even if the request is
191
* canceled by the client, the implementor handling the request should
192
* still call {@link #done()} in order to complete handling
193
* of the request monitor.
195
public synchronized boolean isCanceled() {
196
return fCanceled || (fParentRequestMonitor != null && fParentRequestMonitor.isCanceled());
200
* Adds the given listener to list of listeners that are notified when this
201
* request monitor is canceled.
203
public synchronized void addCancelListener(ICanceledListener listener) {
204
if (fCancelListeners == null) {
205
fCancelListeners = new ListenerList();
207
fCancelListeners.add(listener);
211
* Removes the given listener from the list of listeners that are notified
212
* when this request monitor is canceled.
214
public synchronized void removeCancelListener(ICanceledListener listener) {
215
if (fCancelListeners != null) {
216
fCancelListeners.remove(listener);
221
* Marks this request as completed. Once this method is called, the
222
* monitor submits a runnable to the DSF Executor to call the
223
* <code>handle...</code> methods.
225
* Note: This method should be called once and only once, for every request
226
* issued. Even if the request was canceled.
229
public synchronized void done() {
233
throw new IllegalStateException("RequestMonitor: " + this + ", done() method called more than once"); //$NON-NLS-1$//$NON-NLS-2$
236
// This RequestMonitor is done, it can no longer be canceled.
237
// We must clear the list of cancelListeners because it causes a
238
// circular reference between parent and child requestMonitor, which
239
// causes a memory leak.
240
fCancelListeners = null;
244
fExecutor.execute(new DsfRunnable() {
246
RequestMonitor.this.handleCompleted();
249
public String toString() {
250
return "Completed: " + RequestMonitor.this.toString(); //$NON-NLS-1$
253
} catch (RejectedExecutionException e) {
254
handleRejectedExecutionException();
259
public String toString() {
260
return "RequestMonitor (" + super.toString() + "): " + getStatus().toString(); //$NON-NLS-1$ //$NON-NLS-2$
264
* Checks whether the given request monitor completed with success or
265
* failure result. If the request monitor was canceled it is considered
266
* that it failed, regardless of the status.
268
public boolean isSuccess() {
269
return !isCanceled() && getStatus().getSeverity() <= IStatus.INFO;
273
* Default handler for the completion of a request. The implementation
274
* calls {@link #handleSuccess()} if the request succeeded, and calls
275
* {@link #handleFailure()} or cancel otherwise.
277
* Note: Sub-classes may override this method.
279
@ConfinedToDsfExecutor("fExecutor")
280
protected void handleCompleted() {
289
* Default handler for a successful the completion of a request. If this
290
* monitor has a parent monitor that was configured by the constructor, that
291
* parent monitor is notified. Otherwise this method does nothing.
292
* {@link #handleFailure()} or cancel otherwise.
294
* Note: Sub-classes may override this method.
296
@ConfinedToDsfExecutor("fExecutor")
297
protected void handleSuccess() {
298
if (fParentRequestMonitor != null) {
299
fParentRequestMonitor.done();
304
* The default implementation of a cancellation or an error result of a
305
* request. The implementation delegates to {@link #handleCancel()} and
306
* {@link #handleErrorOrWarning()} as needed.
308
* Note: Sub-classes may override this method.
310
@ConfinedToDsfExecutor("fExecutor")
311
protected void handleFailure() {
317
if (getStatus().getSeverity() == IStatus.CANCEL) {
318
DsfPlugin.getDefault().getLog().log(new Status(
319
IStatus.ERROR, DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Request monitor: '" + this + "' resulted in a cancel status: " + getStatus() + ", even though the request is not set to cancel.", null)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
321
handleErrorOrWarning();
326
* The default implementation of an error or warning result of a request.
327
* The implementation delegates to {@link #handleError()} and
328
* {@link #handleWarning()} as needed.
330
* Note: Sub-classes may override this method.
332
@ConfinedToDsfExecutor("fExecutor")
333
protected void handleErrorOrWarning() {
334
if (getStatus().getSeverity() == IStatus.ERROR) {
342
* The default implementation of an error result of a request. If this
343
* monitor has a parent monitor that was configured by the constructor, that
344
* parent monitor is configured with a new status containing this error.
345
* Otherwise the error is logged.
347
* Note: Sub-classes may override this method.
349
@ConfinedToDsfExecutor("fExecutor")
350
protected void handleError() {
351
if (fParentRequestMonitor != null) {
352
fParentRequestMonitor.setStatus(getStatus());
353
fParentRequestMonitor.done();
355
MultiStatus logStatus = new MultiStatus(DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Request for monitor: '" + toString() + "' resulted in an error.", null); //$NON-NLS-1$ //$NON-NLS-2$
356
logStatus.merge(getStatus());
357
DsfPlugin.getDefault().getLog().log(logStatus);
362
* The default implementation of an error result of a request. If this
363
* monitor has a parent monitor that was configured by the constructor, that
364
* parent monitor is configured with a new status containing this warning.
365
* Otherwise the warning is logged.
367
* Note: Sub-classes may override this method.
369
@ConfinedToDsfExecutor("fExecutor")
370
protected void handleWarning() {
371
if (fParentRequestMonitor != null) {
372
fParentRequestMonitor.setStatus(getStatus());
373
fParentRequestMonitor.done();
378
* Default handler for a canceled the completion of a request. If this
379
* monitor has a parent monitor that was configured by the constructor, that
380
* parent monitor is notified. Otherwise this method does nothing.
382
* Note: Sub-classes may override this method.
384
@ConfinedToDsfExecutor("fExecutor")
385
protected void handleCancel() {
386
if (fParentRequestMonitor != null) {
387
if (getStatus().getSeverity() == IStatus.CANCEL && !fParentRequestMonitor.isCanceled()) {
388
fParentRequestMonitor.setStatus(new Status(
389
IStatus.ERROR, DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Sub-request " + toString() + " was canceled and not handled.'", null)); //$NON-NLS-1$ //$NON-NLS-2$
391
fParentRequestMonitor.setStatus(getStatus());
393
fParentRequestMonitor.done();
398
* Default handler for when the executor supplied in the constructor
399
* rejects the runnable that is submitted invoke this request monitor.
400
* This usually happens only when the executor is shutting down.
402
protected void handleRejectedExecutionException() {
403
MultiStatus logStatus = new MultiStatus(DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Request for monitor: '" + toString() + "' resulted in a rejected execution exception.", null); //$NON-NLS-1$ //$NON-NLS-2$
404
logStatus.merge(getStatus());
405
if (fParentRequestMonitor != null) {
406
fParentRequestMonitor.setStatus(logStatus);
407
fParentRequestMonitor.done();
409
DsfPlugin.getDefault().getLog().log(logStatus);