KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > versioning > system > cvss > ExecutorGroup


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.versioning.system.cvss;
21
22 import org.openide.util.Cancellable;
23 import org.openide.util.NbBundle;
24 import org.openide.ErrorManager;
25 import org.netbeans.api.progress.ProgressHandle;
26 import org.netbeans.api.progress.ProgressHandleFactory;
27
28 import javax.swing.*;
29 import java.util.*;
30 import java.awt.event.ActionEvent JavaDoc;
31
32 /**
33  * Support for actions that run multiple commands.
34  * Represents context that carry data shared by
35  * action commands executors. It can manage execution
36  * in multiple ClientRuntimes (threads).
37  *
38  * <p>Implements shared progress, logging support
39  * and cancelling.
40  *
41  * TODO add consolidated error reporting, nowadays, multiple errors from backgroud thread can popup
42  *
43  * @author Petr Kuzel
44  */

45 public final class ExecutorGroup extends AbstractAction implements Cancellable {
46
47     private final String JavaDoc name;
48     private final boolean abortOnExecutorFailure;
49     public boolean executed;
50     private boolean cancelled;
51     private List listeners = new ArrayList(2);
52     private List executors = new ArrayList(2);
53     private List cleanups = new ArrayList(2);
54     /** ClientRuntime => CommandRunnale*/
55     private Map queues = new HashMap();
56     /** ClientRuntimes*/
57     private Set started = new HashSet();
58     /**
59      * Porgress handle is never created if the group is non-interactive.
60      */

61     private ProgressHandle progressHandle;
62     private long dataCounter;
63     private boolean hasBarrier;
64     private boolean failed;
65     private boolean executingCleanup;
66     private boolean nonInteractive;
67
68     /**
69      * Creates new group.
70      *
71      * @param displayName
72      * Defines prefered display name - localized string that should highlight
73      * group purpose (i.e. in English use verb in gerund).
74      * E.g. <code>UpdateCommand</code> used to refresh statuses should
75      * be named "Refreshing Status" rather than "cvs -N update",
76      * "Updating" or "Status Refresh".
77      */

78     public ExecutorGroup(String JavaDoc displayName) {
79         this(displayName, true);
80     }
81
82     public ExecutorGroup(String JavaDoc displayName, boolean abortOnExecutorFailure) {
83         name = displayName;
84         this.abortOnExecutorFailure = abortOnExecutorFailure;
85     }
86     
87     /**
88      * Defines group display name.
89      */

90     public String JavaDoc getDisplayName() {
91         return name;
92     }
93
94     /**
95      * Starts associated progress if not yet started. Allows to share
96      * progress with execution preparation phase (cache ops).
97      *
98      * @param details progress detail messag eor null
99      */

100     public synchronized void progress(String JavaDoc details) {
101         if (nonInteractive) return;
102         if (progressHandle == null) {
103             progressHandle = ProgressHandleFactory.createHandle(NbBundle.getMessage(ExecutorGroup.class, "BK2001", name), this, this);
104             progressHandle.start();
105         }
106
107         if (details != null) {
108             progressHandle.progress(details);
109         }
110     }
111
112
113     /**
114      * Called by ExecutorSupport on enqueue.
115      * Pairs with finished.
116      *
117      * @param queue processign queue or null for all
118      * @param id identifier paired with {@link #finished}
119      */

120     synchronized void enqueued(ClientRuntime queue, Object JavaDoc id) {
121         progress(null);
122         if (progressHandle != null && started.size() == 0) {
123             progressHandle.setDisplayName(NbBundle.getMessage(ExecutorGroup.class, "BK2005", name));
124             progressHandle.progress(NbBundle.getMessage(ExecutorGroup.class, "BK1007"));
125             progressHandle.switchToDeterminate(100);
126             progressHandle.progress(1);
127         }
128
129         Collection keys;
130         if (queue == null) {
131             keys = queues.keySet();
132         } else {
133             keys = Collections.singleton(queue);
134         }
135
136         Iterator it = keys.iterator();
137         while (it.hasNext()) {
138             Object JavaDoc key = it.next();
139             Set commands = (Set) queues.get(key);
140             if (commands == null) {
141                 commands = new HashSet();
142             }
143             commands.add(id);
144             queues.put(key, commands);
145         }
146     }
147
148     /**
149      * Called by ExecutorSupport on start.
150      */

151     synchronized void started(ClientRuntime queue) {
152
153         if (progressHandle != null) {
154             progressHandle.switchToIndeterminate();
155             progressHandle.setDisplayName(NbBundle.getMessage(ExecutorGroup.class, "BK2001", name));
156         }
157
158         if (!nonInteractive && started.add(queue)) {
159             String JavaDoc msg = NbBundle.getMessage(ExecutorGroup.class, "BK1001", new Date(), getDisplayName());
160             String JavaDoc sep = NbBundle.getMessage(ExecutorGroup.class, "BK1000");
161             String JavaDoc header = "\n" + sep + "\n" + msg + "\n"; // NOI18N
162
queue.log(header);
163         }
164     }
165
166     /**
167      * Called by ExecutorSupport after processing.
168      *
169      * @param queue processign queue or null for all
170      * @param id identifier paired with {@link #enqueued(ClientRuntime, Object)}
171      */

172     synchronized void finished(ClientRuntime queue, Object JavaDoc id) {
173
174         Collection keys;
175         if (queue == null) {
176             keys = new HashSet(queues.keySet());
177         } else {
178             keys = Collections.singleton(queue);
179         }
180
181         boolean finished = executed; // TODO how to tip true for non-executed?
182
Iterator it = keys.iterator();
183         while (it.hasNext()) {
184             Object JavaDoc key = it.next();
185
186             Set commands = (Set) queues.get(key);
187             commands.remove(id);
188             if (commands.isEmpty()) {
189                 queues.remove(key);
190                 if (executed && queues.isEmpty() && progressHandle != null) {
191                     progressHandle.finish();
192                     progressHandle = null;
193                 }
194             }
195             finished &= commands.isEmpty();
196         }
197
198         if (finished) {
199             logFinished(queue);
200         }
201     }
202
203     private void logFinished(ClientRuntime queue) {
204         if (nonInteractive) return;
205         Collection consoles;
206         if (queue == null) {
207             consoles = started;
208         } else {
209             consoles = Collections.singleton(queue);
210         }
211
212         String JavaDoc msg;
213         if (isCancelled()) {
214             msg = NbBundle.getMessage(ExecutorGroup.class, "BK1006", new Date(), getDisplayName());
215         } else {
216             msg = NbBundle.getMessage(ExecutorGroup.class, "BK1002", new Date(), getDisplayName());
217         }
218
219         Iterator it2 = consoles.iterator();
220         while (it2.hasNext()) {
221             ClientRuntime console = (ClientRuntime) it2.next();
222             console.log(msg + "\n"); // NOI18N
223
console.flushLog();
224         }
225     }
226
227     public boolean isCancelled() {
228         return cancelled;
229     }
230
231     /**
232      * User cancel comming from Progress UI.
233      * Must not be called by internals.
234      */

235     public boolean cancel() {
236         cancelled = true;
237         fail();
238         return true;
239     }
240
241     /**
242      * A command in group failed. Stop all pending commands if abortOnExecutorFailure is set.
243      */

244     public void fail() {
245         if (!abortOnExecutorFailure) return;
246         failed = true;
247         Iterator it;
248         synchronized(listeners) {
249             it = new ArrayList(listeners).iterator();
250         }
251         while (it.hasNext()) {
252             try {
253                 Cancellable cancellable = (Cancellable) it.next();
254                 cancellable.cancel();
255             } catch (RuntimeException JavaDoc ex) {
256                 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex);
257             }
258         }
259         synchronized(this) {
260             if (progressHandle != null) {
261                 progressHandle.finish();
262                 progressHandle = null;
263             }
264         }
265     }
266
267     /**
268      * Add a cancelaable in chain of cancellable performers.
269      */

270     public void addCancellable(Cancellable cancellable) {
271         synchronized(listeners) {
272             listeners.add(cancellable);
273         }
274     }
275
276     public void removeCancellable(Cancellable cancellable) {
277         synchronized(listeners) {
278             listeners.remove(cancellable);
279         }
280     }
281
282     /**
283      * Add executor into this group.
284      */

285     public synchronized void addExecutor(ExecutorSupport executor) {
286         assert executed == false;
287         executor.joinGroup(this); // XXX third party code executed under lock
288
executors.add(executor);
289     }
290
291     /**
292      * Add executors into this group.
293      * @param executors groupable or <code>null</code>
294      */

295     public final synchronized void addExecutors(ExecutorSupport[] executors) {
296         if (executors == null) {
297             return;
298         } else {
299             for (int i = 0; i < executors.length; i++) {
300                 ExecutorSupport support = executors[i];
301                 addExecutor(support);
302             }
303         }
304     }
305
306     /**
307      * Group execution blocks on this barier until
308      * all previously added Groupable finishes (succesfuly or with fail).
309      *
310      * <p>Warning: Groups with barries have blocking {@link #execute},
311      * there is assert banning to execute such group from UI thread.
312      */

313     public synchronized void addBarrier(Runnable JavaDoc action) {
314         assert executed == false;
315         ExecutorGroupBar bar = new ExecutorGroupBar(executors, action);
316         bar.joinGroup(this);
317         executors.add(bar);
318         hasBarrier = true;
319     }
320
321     /**
322      * Can be added only from barrier action!
323      */

324     public synchronized void addCleanups(ExecutorSupport[] executors) {
325         if (executors == null) {
326             return;
327         } else {
328             for (int i = 0; i < executors.length; i++) {
329                 ExecutorSupport support = executors[i];
330                 addCleanup(support);
331             }
332         }
333     }
334
335     /**
336      * Can be added only from barrier action!
337      */

338     public synchronized void addCleanup(ExecutorSupport executor) {
339         assert executingCleanup == false;
340         executor.joinGroup(this);
341         cleanups.add(executor);
342     }
343
344     /**
345      * Asynchronously executes all added executors. Executors
346      * are grouped according to CVSRoot and serialized in
347      * particular ClientRuntime (thread) queue. It maintains
348      *
349      * <p>Warning:
350      * <ul>
351      * <li>It becomes blocking if group contains barriers (there is UI thread assert).
352      * <li>Do not call {@link ExecutorSupport#execute} if you
353      * use grouping.
354      * </ul>
355      */

356     public void execute() {
357         assert (SwingUtilities.isEventDispatchThread() && hasBarrier) == false;
358
359         synchronized(this) {
360             executed = true;
361         }
362         Iterator it = executors.iterator();
363         int i = 0;
364         while (it.hasNext()) {
365             Groupable support = (Groupable) it.next();
366             try {
367                 support.execute();
368             } catch (Error JavaDoc err) {
369                 ErrorManager.getDefault().notify(err);
370                 fail();
371             } catch (RuntimeException JavaDoc ex) {
372                 ErrorManager.getDefault().notify(ex);
373                 fail();
374             }
375             i++;
376             if (failed) break;
377         }
378
379         // cleanup actions
380

381         synchronized(this) {
382             executingCleanup = true;
383         }
384         it = cleanups.iterator();
385         while (it.hasNext()) {
386             Groupable support = (Groupable) it.next();
387             support.execute();
388             i++;
389         }
390
391         synchronized(this) {
392             if (i == 0 && progressHandle != null) { // kill progress provoked by progress()
393
progressHandle.finish();
394                 progressHandle = null;
395             }
396         }
397     }
398
399     /**
400      * Allows clients that execute grouped suppors
401      * synchronously communicate their assumtion that
402      * all supporst in group have been executed. It's
403      * time for progress cleanup and logging finished
404      * messages.
405      */

406     public synchronized void executed() {
407         if (executed == false) {
408             if (progressHandle != null) {
409                 progressHandle.finish();
410                 progressHandle = null;
411             }
412             logFinished(null);
413         }
414     }
415
416     synchronized void increaseDataCounter(long bytes) {
417         dataCounter += bytes;
418         if (progressHandle != null) { // dangling event from zombie worker thread
419
progressHandle.progress(NbBundle.getMessage(ExecutorGroup.class, "BK2002", name, format(dataCounter)));
420         }
421     }
422
423     private static String JavaDoc format(long counter) {
424         if (counter < 1024*16) {
425             return NbBundle.getMessage(ExecutorGroup.class, "BK2003", new Long JavaDoc(counter));
426         }
427         counter /= 1024;
428         return NbBundle.getMessage(ExecutorGroup.class, "BK2004", new Long JavaDoc(counter));
429
430         // do not go to megabytes as user want to see CHANGING number
431
// it can be solved by average speed in last 5sec, as it drops to zero
432
// something is wrong
433
}
434
435     /**
436      * Link action. Take random output, inmost coses one anyway.
437      */

438     public void actionPerformed(ActionEvent JavaDoc e) {
439         if (queues != null) {
440             Set keys = queues.keySet();
441             if (keys.isEmpty() == false) {
442                 ClientRuntime queue = (ClientRuntime) keys.iterator().next();
443                 queue.focusLog();
444             }
445         }
446     }
447
448     public void setNonInteractive(boolean nonInteractive) {
449         this.nonInteractive = nonInteractive;
450     }
451
452     public static interface Groupable {
453
454         /**
455          * Notifies the Groupable that it is a part of
456          * given execution chain.
457          *
458          * <p> Must be called before {@link #execute}
459          */

460         void joinGroup(ExecutorGroup group);
461
462         /** Execute custom code. */
463         void execute();
464     }
465 }
466
Popular Tags