KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > j2ee > common > EventRequestProcessor


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.j2ee.common;
21
22 import java.awt.event.ActionEvent JavaDoc;
23 import java.awt.event.ActionListener JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Collection JavaDoc;
26 import java.util.List JavaDoc;
27 import javax.swing.JComponent JavaDoc;
28 import javax.swing.SwingUtilities JavaDoc;
29 import org.netbeans.api.progress.ProgressHandle;
30 import org.netbeans.api.progress.ProgressHandleFactory;
31 import org.openide.ErrorManager;
32 import org.openide.util.Cancellable;
33 import org.openide.util.Mutex;
34 import org.openide.util.RequestProcessor;
35
36 /**
37  * A class providing support for running synchronous (in the event dispathing
38  * thread) and asynchronous (outside the EDT) actions. Multiple
39  * actions can be run at the same time, switching between synchronous and
40  * asynchronous ones. A progress panel is displayed for asynchronous actions.
41  *
42  * <p>A typical use case is running an asynchronous action with a progress dialog.
43  * For that just create an {@link #AsynchronouosAction} and send it to the {@link #invoke} method.</p>
44  *
45  * <p>A more complex use case is mixing actions: first you need to run an asynchronous
46  * action, the a synchronous one (but in certain cases only) and then another
47  * asynchronous one, showing and hiding the progress panel as necessary.</p>
48  *
49  * @author Andrei Badea
50  */

51 public class EventRequestProcessor {
52
53     // PENDING it may be worth to split Action into:
54
// - final class ActionDescriptor containing the enabled and runInEventThread properties
55
// (or consider using/extending org.openide.util.Task instead if possible -- maybe not, since it runs Runnable's)
56
// - interface Action containing invoke(Context actionContext)
57

58     // PENDING should use own RequestProcessor, not the default one
59

60     private static final ErrorManager LOGGER = ErrorManager.getDefault().getInstance("org.netbeans.modules.j2ee.persistence.util"); // NOI18N
61
private static final boolean LOG = LOGGER.isLoggable(ErrorManager.INFORMATIONAL);
62
63     // not private because used in tests
64
ActionInvoker actionInvoker;
65
66     public EventRequestProcessor() {
67     }
68
69     public void invoke(Collection JavaDoc<Action> actions) {
70         invoke(actions, false);
71     }
72
73     /**
74      * Returns true if all actions were invoked, false otherwise
75      * (e.g. if the invocation was cancelled).
76      */

77     public boolean invoke(Collection JavaDoc<Action> actions, boolean cancellable) {
78         if (!SwingUtilities.isEventDispatchThread()) {
79             throw new IllegalStateException JavaDoc("This method must be called in the event thread."); // NOI18N
80
}
81         if (this.actionInvoker != null) {
82             throw new IllegalStateException JavaDoc("The invoke() method is running."); // NOI18N
83
}
84
85         actionInvoker = new ActionInvoker(new ArrayList JavaDoc<Action>(actions), cancellable);
86         boolean success;
87
88         try {
89             success = actionInvoker.invoke();
90         } finally {
91             actionInvoker = null;
92         }
93
94         return success;
95     }
96
97     private static final class ActionInvoker implements ActionListener JavaDoc {
98
99         private final List JavaDoc<Action> actions;
100         private final boolean cancellable;
101
102         private Context actionContext;
103         private int currentActionIndex;
104         private boolean cancelled;
105
106         public ActionInvoker(List JavaDoc<Action> actions, boolean cancellable) {
107             this.actions = actions;
108             this.cancellable = cancellable;
109         }
110
111         /**
112          * Returns true if all actions were invoked, false otherwise
113          * (e.g. if the invocation was cancelled).
114          */

115         public boolean invoke() {
116             assert SwingUtilities.isEventDispatchThread();
117
118             final ProgressPanel progressPanel = new ProgressPanel();
119             progressPanel.setCancelVisible(cancellable);
120             progressPanel.addCancelActionListener(this);
121
122             ProgressHandle progressHandle = ProgressHandleFactory.createHandle(null);
123             JComponent JavaDoc progressComponent = ProgressHandleFactory.createProgressComponent(progressHandle);
124             progressHandle.start();
125             progressHandle.switchToIndeterminate();
126
127             actionContext = new Context(new Progress(progressPanel, progressHandle));
128
129             // exceptions[0] contains the exception, if any, thrown by an action invocation
130
// in either EDT or the RP thread
131
final Throwable JavaDoc[] exceptions = new Throwable JavaDoc[1];
132
133             try {
134
135                 RequestProcessor.Task task = RequestProcessor.getDefault().create(new Runnable JavaDoc() {
136                     public void run() {
137                         try {
138                             invokeActionsUntilThreadSwitch();
139                         } catch (Throwable JavaDoc t) {
140                             exceptions[0] = t;
141                         } finally {
142                             SwingUtilities.invokeLater(new Runnable JavaDoc() {
143                                 public void run() {
144                                     progressPanel.close();
145                                 }
146                             });
147                         }
148                     }
149                 });
150
151                 boolean runInEDT = true;
152
153                 for (;;) {
154
155                     if (currentActionIndex >= actions.size() || cancelled) {
156                         break;
157                     }
158
159                     try {
160                         if (!runInEDT) {
161                             // the action at currentActionIndex is an asynchronous one
162
// must be run outside EDT
163
task.schedule(0);
164                             progressPanel.open(progressComponent);
165                         }
166
167                         invokeActionsUntilThreadSwitch();
168
169                         if (!runInEDT) {
170                             // the RP might be still running (e.g. preempted by the AWT thread
171
// thread just after the SW.invokeLater())
172
// also ensures correct visibility of the fields
173
// modified by the RP thread
174
task.waitFinished();
175                         }
176                     } catch (Throwable JavaDoc t) {
177                         exceptions[0] = t;
178                     }
179
180                     if (exceptions[0] != null) {
181                         if (exceptions[0] instanceof RuntimeException JavaDoc) {
182                             throw (RuntimeException JavaDoc)exceptions[0];
183                         } else {
184                             RuntimeException JavaDoc exception = new RuntimeException JavaDoc(exceptions[0].getMessage());
185                             exception.initCause(exceptions[0]);
186                             throw exception;
187                         }
188                     }
189
190                     runInEDT = !runInEDT;
191                 }
192             } finally {
193                 progressHandle.finish();
194             }
195
196             return !cancelled;
197         }
198
199         private void invokeActionsUntilThreadSwitch() {
200             boolean isEventThread = SwingUtilities.isEventDispatchThread();
201
202             for (;;) {
203
204                 synchronized (this) {
205                     // synchronized because cancelled can be set at any time
206
// by actionPerformed() in the EDT
207
if (cancelled) {
208                         break;
209                     }
210                 }
211
212                 if (currentActionIndex >= actions.size()) {
213                     break;
214                 }
215
216                 Action action = actions.get(currentActionIndex);
217                 if (!action.isEnabled()) {
218                     if (LOG) {
219                         LOGGER.log("Skipping " + action); // NOI18N
220
}
221                     synchronized (this) {
222                         // synchronizd for the AWT thread to be able to read it
223
// in actionPerformed()
224
currentActionIndex++;
225                     }
226                     continue;
227                 }
228
229                 if (action.getRunInEventThread() != isEventThread) {
230                     break;
231                 }
232
233                 if (LOG) {
234                     LOGGER.log("Running " + action); // NOI18N
235
}
236
237                 // only enable/disable the cancel button for async actions
238
if (!isEventThread) {
239                     final boolean cancelEnabled = action instanceof Cancellable;
240                     SwingUtilities.invokeLater(new Runnable JavaDoc() {
241                         public void run() {
242                             actionContext.getProgress().getPanel().setCancelEnabled(cancelEnabled);
243                         }
244                     });
245                 }
246
247                 action.run(actionContext);
248
249                 synchronized (this) {
250                     // synchronizd for the AWT thread to be able to read it
251
// in actionPerformed()
252
currentActionIndex++;
253                 }
254             }
255
256         }
257
258         public void actionPerformed(ActionEvent JavaDoc event) {
259             synchronized (this) {
260
261                 // just in case the user managed to click Cancel twice
262
if (cancelled) {
263                     return;
264                 }
265
266                 // all actions could have been invoked by now
267
if (currentActionIndex >= actions.size()) {
268                     return;
269                 }
270
271                 Action currentAction = actions.get(currentActionIndex);
272
273                 // there is no guarantee that currentAction is asynchronous or that it
274
// implements Cancellable (maybe the action before it did and the user clicked Cancel
275
// just before it finished). If it doesn't we can't do better than
276
// just ignore the Cancel request.
277
if (!currentAction.getRunInEventThread() && currentAction instanceof Cancellable) {
278                     // calling cancel() in the EDT although the action was async
279
cancelled = ((Cancellable)currentAction).cancel();
280                     if (cancelled) {
281                         actionContext.getProgress().getPanel().setCancelEnabled(false);
282                     }
283                 }
284             }
285         }
286     }
287
288     /**
289      * This class encapsulates the context in which the actions are run.
290      * It can be used to obtain a {@link Progress} instance.
291      */

292     public static final class Context {
293
294         private final Progress progress;
295
296         public Context(Progress progress) {
297             this.progress = progress;
298         }
299
300         public Progress getProgress() {
301             return progress;
302         }
303     }
304
305     /**
306      * This class is used to give information about the progress of the actions.
307      */

308     public static final class Progress {
309
310         private final ProgressPanel panel;
311         private final ProgressHandle handle;
312
313         private Progress(ProgressPanel panel, ProgressHandle handle) {
314             this.panel = panel;
315             this.handle = handle;
316         }
317
318         public void switchToDeterminate(int workunits) {
319             handle.switchToDeterminate(workunits);
320         }
321
322         public void progress(final String JavaDoc message) {
323             Mutex.EVENT.readAccess(new Runnable JavaDoc() {
324                 public void run() {
325                     panel.setText(message);
326                 }
327             });
328             handle.progress(message);
329         }
330
331         public void progress(int workunit) {
332             handle.progress(workunit);
333         }
334
335         /**
336          * Used in tests.
337          */

338         ProgressPanel getPanel() {
339             return panel;
340         }
341     }
342
343     /**
344      * Describes an action. See also {@link Synchronous} and
345      * {@link Asynchronous}.
346      */

347     public interface Action {
348
349         /**
350          * Returns true if the action should be run in the EDT and
351          * false otherwise.
352          */

353         public boolean getRunInEventThread();
354
355         /**
356          * Returns true if the actions is enabled (should be run). This is
357          * useful when having e.g. a {@link Asynchronous asynchronous}
358          * action between two {@link Synchronous synchronous} actions. If the
359          * asynchronous action is false the progress dialog
360          * is not displayed at all (if it was displayed it would just blink
361          * for a short time, which does not look good).
362          */

363         public boolean isEnabled();
364
365         /**
366          * This method is invoked when the action should be run.
367          */

368         public void run(Context actionContext);
369     }
370
371     public interface CancellableAction extends Action, Cancellable {
372
373     }
374
375     /**
376      * Describes a synchronous action, that is, one that should be run
377      * in the EDT.
378      */

379     public static abstract class SynchronousAction implements Action {
380
381         public boolean getRunInEventThread() {
382             return true;
383         }
384
385         public boolean isEnabled() {
386             return true;
387         }
388     }
389
390     /**
391      * Describes an asynchronous action, that is, one that should be run
392      * outside the EDT and with a progress dialog.
393      */

394     public static abstract class AsynchronousAction implements Action {
395
396         public boolean getRunInEventThread() {
397             return false;
398         }
399
400         public boolean isEnabled() {
401             return true;
402         }
403     }
404 }
405
Popular Tags