KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > j2ee > persistence > util > 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.persistence.util;
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  * <p>
50  * <i>This class is a copy of the <code>org.netbeans.modules.j2ee.common.EventRequestProcessor</code>,
51  * which can't be directly used because of dependencies. If a more appropriate place within
52  * the IDE cluster is found for the original class, this copy should be removed.</i>
53  * </p>
54  *
55  * @author Andrei Badea
56  */

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

64     // PENDING should use own RequestProcessor, not the default one
65

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

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

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

298     public static final class Context {
299
300         private final Progress progress;
301
302         public Context(Progress progress) {
303             this.progress = progress;
304         }
305
306         public Progress getProgress() {
307             return progress;
308         }
309     }
310
311     /**
312      * This class is used to give information about the progress of the actions.
313      */

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

344         ProgressPanel getPanel() {
345             return panel;
346         }
347     }
348
349     /**
350      * Describes an action. See also {@link Synchronous} and
351      * {@link Asynchronous}.
352      */

353     public interface Action {
354
355         /**
356          * Returns true if the action should be run in the EDT and
357          * false otherwise.
358          */

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

369         public boolean isEnabled();
370
371         /**
372          * This method is invoked when the action should be run.
373          */

374         public void run(Context actionContext);
375     }
376
377     public interface CancellableAction extends Action, Cancellable {
378
379     }
380
381     /**
382      * Describes a synchronous action, that is, one that should be run
383      * in the EDT.
384      */

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

400     public static abstract class AsynchronousAction implements Action {
401
402         public boolean getRunInEventThread() {
403             return false;
404         }
405
406         public boolean isEnabled() {
407             return true;
408         }
409     }
410 }
411
Popular Tags