/[debian]/libspin-java/branches/NM/docs/index.html
ViewVC logotype

Contents of /libspin-java/branches/NM/docs/index.html

Parent Directory Parent Directory | Revision Log Revision Log


Revision 954 - (show annotations)
Tue Oct 2 16:34:15 2007 UTC (13 years, 11 months ago) by gregoa
File MIME type: text/html
File size: 24467 byte(s)
revert injection of new upstream release
1 <!-- saved from url=(0022)http://internet.e-mail -->
2 <html>
3
4 <head>
5 <title>Spin your Swing</title>
6 <meta name="description"
7 content="Spin transparent threading solution for Swing" />
8 <meta name="keywords"
9 content="Spin, Java, Swing, Thread,EventDispatcher, EventDispatchThread,
10 EventQueue,SwingWorker,SwingUtilities,invokeLater,
11 invokeAndWait" />
12 <style type="text/css">
13 <!--
14 *.EDT { color:#00dd00 }
15 *.notEDT { color:#dd0000 }
16 *.GUI { background-color:#ddffdd }
17 *.notGUI { background-color:#ffdddd }
18 td.SPIN { background-color:#ffffdd }
19 span.SPIN{ color:#bbbb00 }
20 -->
21 </style>
22 </head>
23
24 <body>
25 <h1>
26 Spin your Swing
27 </h1>
28 <h2>
29 Abstract
30 </h2>
31 <p align="center">
32 <cite><em>Spin</em> is a transparent threading solution for non-freezing
33 Swing applications.</cite>
34 </p>
35 <p>
36 Swing is the standard component architecture for developing Java desktop
37 applications. Its exhaustive collection of widgets is the foundation for
38 easy development of rich graphical user interfaces (GUI).<br />
39 Alas every non trivial GUI sooner or later encounters the problem of
40 "freeze".
41 This annoying behaviour is experienced by users every time the application
42 performs extensive calculations or blocks for network or disk I/O.
43 </p>
44 <p>
45 In this document we will explain the reason for this problem and explore
46 different techniques to prevent Swing GUIs from "freezing".
47 We will present our project named <em>Spin</em> which offers a - to our
48 knowledge - revolutionary new approach.
49 It offers transparent thread handling with minimal impact on your application
50 code.
51 </p>
52 <p>
53 <em>Spin</em> is hosted at <a href="http://sourceforge.net/projects/spin/">
54 <img align="top" src="http://sourceforge.net/sflogo.php?group_id=3636&type=2"
55 width="125" height="37" border="0" alt="SourceForge.net Logo" />
56 </a>
57 </p>
58
59 <h2>
60 Standard Swing
61 </h2>
62 <p>
63 In this section we take a look at a naive GUI implementation that shows how
64 Swing freezes in case the application programmer doesn't take any special
65 actions against it.
66 We also describe the problem of calls into Swing components triggered by any
67 other thread than the event dispatch thread.
68 <br/>
69 Swing is not designed for multi threading, so let us first recall the single
70 threading rule of every Swing GUI:
71 </p>
72 <p align="center">
73 <cite>Access to a (visible) Swing component has to occur in the event dispatch
74 thread.</cite>
75 </p>
76 <p>
77 The event dispatch thread (short EDT) is responsible to process all GUI related
78 events, e.g. notifying listeners of user input, repainting dirty regions or
79 updated areas.
80 All these events are enqueued and treated sequentially - if one of them takes
81 a long time to be processed, all further events will have to wait.
82 </p>
83 <p>
84 In the tables throughout this document the left column represents a Swing GUI
85 component, the right column represents a non visual multithreaded bean that
86 encapsulates extensive calculations.
87 Code run on the EDT is <span class="EDT">shown in green</span> and code
88 called by any other thread is <span class="notEDT">shown in red</span>.
89 </p>
90 <p>
91 As you can see in the upper half of the following table, the GUI calls the
92 method <code>getValue()</code> on the bean when an action is performed.
93 The event dispatch thread is routed from the GUI to the bean. While it is
94 performing its calculations no further swing events can be processed - the GUI
95 freezes.<br/>
96 One of these queued events is the repaint of the label triggered by
97 <code>label.setText("...")</code>. When <code>getValue()</code> returns, the
98 text of the label is changed again before the previous text was painted.
99 So in fact <code>"..."</code> is never seen:
100 </p>
101 <a name="standard" />
102 <table border="1" cellpadding="4" cellspacing="0" width="100%">
103 <tr valign="top">
104 <td width="50%" class="GUI">
105 GUI
106 </td>
107 <td width="50%" class="notGUI">
108 BeanImpl
109 </td>
110 </tr>
111 <tr valign="top">
112 <td>
113 <pre>
114 public void actionPerformed(ActionEvent e)
115 {<span class="EDT">
116 label.setText("...");
117 label.setText(bean.getValue());
118 </span>}
119
120
121
122
123 public void propertyChange(PropertyChangeEvent ev)
124 {<span class="notEDT">
125 label.setText((String)ev.getNewValue());
126 </span>}
127 </pre>
128 </td>
129 <td>
130 <pre>
131 public String getValue()
132 {<span class="EDT">
133 String value;
134
135 // extensive calculation
136
137 return value;
138 </span>}
139
140 public void setValue(String value)
141 {<span class="notEDT">
142 this.value = value;
143 firePropertyChange(value);
144 </span>}
145 </pre>
146 </td>
147 </tr>
148 </table>
149 <p>
150 The lower half of the table shows what happens if <code>setValue()</code> is
151 invoked on the bean on another thread. The listeners are notified (GUI
152 implements <code>java.beans.PropertyChangeListener</code> and is registered as
153 a listener for changes of the bean), triggering a call to
154 <code>propertyChange()</code> on the GUI. The text of the label is altered
155 on the calling thread, violating the Swing threading rule.
156 </p>
157 <p>
158 The color distribution gives a hint where to search for problems of this
159 implementation:<br/>
160 <span class="EDT">Green</span> rows of code in the right column result in a
161 GUI freeze, <span class="notEDT">red</span> rows in left column show a
162 violation to the Swing threading rule.
163 </p>
164
165 <h2>
166 Working Thread
167 </h2>
168 <p>
169 One obvious solution to the problems seen in the previous section is to shift
170 the invocation of <code>getValue()</code> from the EDT to a separate thread.
171 When this method returns we must not propagate the result to a Swing component
172 though. We have to return control to the EDT instead. This can be achieved via
173 <code>SwingUtilities.invokeLater()</code> which will use our Runnable to
174 correctly change the label's text on the EDT:
175 </p>
176
177 <table border="1" cellpadding="4" cellspacing="0" width="100%">
178 <tr valign="top">
179 <td width="50%" class="GUI">
180 GUI
181 </td>
182 <td width="50%" class="notGUI">
183 BeanImpl
184 </td>
185 </tr>
186 <tr valign="top">
187 <td>
188 <pre>
189 public void actionPerformed(ActionEvent e)
190 {<span class="EDT">
191 label.setText("...");
192 new Thread(new Runnable()</span>
193 {
194 public void run()
195 {<span class="notEDT">
196 final String value = bean.getValue();
197 SwingUtilities.invokeLater(new Runnable()</span>
198 {
199 public void run()
200 {
201 <span class="EDT">label.setText(value);</span>
202 }
203 }<span class="notEDT">);</span>
204 }
205 }<span class="EDT">).start();</span>
206 }
207
208 public void propertyChange(final PropertyChangeEvent ev)
209 {
210 <span class="notEDT">SwingUtilities.invokeAndWait(new Runnable()</span>
211 {
212 public void run()
213 {
214 <span class="EDT">label.setText((String)ev.getNewValue());</span>
215 }
216 }<span class="notEDT">);</span>
217 }
218 </pre>
219 </td>
220 <td>
221 <pre>
222 public String getValue()
223 {<span class="notEDT">
224 String value;
225
226 // extensive calculation
227
228 return value;
229 </span>}
230
231
232
233
234
235
236
237
238
239
240
241 public void setValue(String value)
242 {<span class="notEDT">
243 this.value = value;
244 firePropertyChange(value);
245 </span>}
246 </pre>
247 </td>
248 </tr>
249 </table>
250 <p>
251 Now what happens if the bean informs the GUI about a value-change triggered
252 by another thread? In <code>propertyChange()</code> we pass a runnable to the
253 EDT via <code>SwingUtiltites.invokeAndWait()</code> that can safely alter the
254 label.
255 </p>
256 <p>
257 Let's take a look at the colors once again:
258 On the right there is only <span class="notEDT">red</span> - so we achieved a
259 non freezing GUI.
260 The left column is <span class="EDT">almost totally green</span>. Since we
261 restrict changes to Swing components to these <span class="EDT">green</span>
262 rows we are honouring the Swing threading rule too.<br/>
263 But the <span class="notEDT">red</span> rows on the left make things
264 difficult:
265 The programmer of the GUI always has to know which thread is stepping through
266 what part of the code - without any visual help of thread-coloring.
267 Any mistake reintroduces the problems mentioned above.
268 </p>
269
270 <h2>
271 SwingWorker
272 </h2>
273 <p>
274 SwingWorker is a utility class that aims to ease the efforts to write a
275 non-freezing GUI. Although not included in the standard Java distribution it is
276 maintained by the Swing team and downloadable at
277 <a href="http://www.theswingconnection.com">The Swing Connection</a>.
278 </p>
279 <p>
280 As you can see in the following table a SwingWorker removes some of the visual
281 clutter seen in the previous section. To use it you have to subclass it,
282 placing extensive calculations into method <code>construct()</code>.
283 In <code>finished()</code> you can alter the label because this method is called
284 on the EDT. This is similar to our previous solution but this time the threading
285 is handled by the superclass:
286 </p>
287 <table border="1" cellpadding="4" cellspacing="0" width="100%">
288 <tr valign="top">
289 <td width="50%" class="GUI">
290 GUI
291 </td>
292 <td width="50%" class="notGUI">
293 BeanImpl
294 </td>
295 </tr>
296 <tr valign="top">
297 <td>
298 <pre>
299 public void actionPerformed(ActionEvent e)
300 {<span class="EDT">
301 label.setText("...");
302 new SwingWorker()</span>
303 {
304 public Object construct()
305 {
306 <span class="notEDT">return bean.getValue();</span>
307 }
308
309 public void finished()
310 {
311 <span class="EDT">label.setText((String)getValue());</span>
312 }
313 }<span class="EDT">).start();</span>
314 }
315
316
317 public void propertyChange(final PropertyChangeEvent ev)
318 {
319 <span class="notEDT">SwingUtilities.invokeAndWait(new Runnable()</span>
320 {
321 public void run()
322 {
323 <span class="EDT">label.setText((String)ev.getNewValue());</span>
324 }
325 }<span class="notEDT">);</span>
326 }
327 </pre>
328 </td>
329 <td>
330 <pre>
331 public String getValue()
332 {<span class="notEDT">
333 String value;
334
335 // extensive calculation
336
337 return value;
338 </span>}
339
340
341
342
343
344
345
346
347
348
349 public void setValue(String value)
350 {<span class="notEDT">
351 this.value = value;
352 firePropertyChange(value);
353 </span>}
354 </pre>
355 </td>
356 </tr>
357 </table>
358 <p>
359 The SwingWorker offers no support for our notification problem so we stick to
360 our previous solution in <code>propertyChange()</code>.
361 </p>
362 <p>
363 What about the colors?<br/>
364 The situation hasn't really improved. The indentation of code was minimized but
365 the <span class="notEDT">red</span> and <span class="EDT">green</span> colors
366 in the GUI stay the same. So the problem above isn't resolved yet.
367 </p>
368
369 <h2>
370 Spin
371 </h2>
372 <p>
373 Now let's take a look at the <em>Spin</em> solution. The following table shows
374 the code:
375 </p>
376 <table border="1" cellpadding="4" cellspacing="0" width="100%">
377 <tr valign="top">
378 <td width="50%" class="GUI">
379 GUI
380 </td>
381 <td rowspan="2" valign="middle" class="SPIN">
382 Spin
383 </td>
384 <td width="50%" class="notGUI">
385 BeanImpl
386 </td>
387 </tr>
388 <tr valign="top">
389 <td>
390 <pre>
391 public void actionPerformed(ActionEvent e)
392 {<span class="EDT">
393 label.setText("...");
394 label.setText(bean.getValue());
395 </span>}
396
397
398
399
400 public void propertyChange(PropertyChangeEvent ev)
401 {
402 <span class="EDT">label.setText((String)ev.getNewValue());</span>
403 }
404 </pre>
405 </td>
406 <td>
407 <pre>
408 public String getValue()
409 {<span class="notEDT">
410 String value;
411
412 // extensive calculation
413
414 return value;
415 </span>}
416
417 public void setValue(String value)
418 {<span class="notEDT">
419 this.value = value;
420 firePropertyChange(value);
421 </span>}
422 </pre>
423 </td>
424 </tr>
425 </table>
426 <p>
427 Hey wait a minute!
428 It's the same code as shown in the <a href="#standard">first table</a>.
429 But the colors have changed - the GUI is completely
430 <span class="EDT">green</span> and the bean is <span class="red">red</span> -
431 how can this be?
432 </p>
433 <p>
434 <em>Spin</em> makes this solution possible - as you can see with no impact on
435 the old code. The <span class="SPIN">yellow</span> column in the middle handles
436 all threading issues transparently.<br/>
437 All we have to do is to <em>spin-off</em> the bean from the EDT. For this we
438 wrap the bean in an instance of type <code>Spin</code>.
439 The result can safely be casted to any interface the bean (or one of its
440 superclasses) implements. The best place to do this is before a reference to
441 this bean is passed to a GUI component (why bother the programmer of the GUI
442 with this little detail):
443 </p>
444 <pre>
445 bean = (Bean)Spin.off(bean);
446 </pre>
447 <p>
448 The only restriction here is that the Bean has to be broken into interface and
449 implementation. The GUI components will only use the interface!
450 The following picture shows how <em>Spin</em> connects the GUI and the bean.
451 Calls on the EDT from the GUI to the bean are brokered to other threads
452 invocating the beans functionality:
453 </p>
454 <p align="center">
455 <img src="spinoff.gif" alt="Spin off"/>
456 </p>
457 <p>
458 The need for an interface isn't really a restriction:
459 <ul>
460 <li>
461 It is generally recommended to separate an application in different layers
462 which communicate through well defined interfaces.
463 The GUI of an application is certainly part of another layer than extensive
464 calculations or I/O operations.
465 </li>
466 <li>
467 If you don't want to or are unable to use an interface you can
468 <a href="#cglib">utilize CGLib instead of JDK proxies</a>.
469 </li>
470 </ul>
471
472 </p>
473 <p>
474 For the notification of changes to the bean we use an inverse technique.
475
476 We must <em>spin-over</em> any invocation of a GUI callback-method on another
477 thread to the EDT. This time we wrap the GUI in a <code>Spin</code> instance
478 assuming that the bean allows for an <code>PropertyChangeListener</code> to
479 be added as a callback (this could be any interface like
480 foo.DataChangedListener):
481 </p>
482 <pre>
483 bean.addPropertyChangeListener((PropertyChangeListener)Spin.over(gui);
484 </pre>
485 <p align="center">
486 <img src="spinover.gif" alt="Spin over"/>
487 </p>
488 <p>
489 This is all you have to know to get <em>Spin</em> to work in your project.
490 If you're interested in the internals of <em>Spin</em> go on to the next
491 section.
492 </p>
493
494 <h2>
495 Internals
496 </h2>
497 <p>
498 <em>Spin</em> is built on top of virtual proxies and a technique borrowed from
499 the java.awt.Dialog component.
500 While a modal dialog has to wait for user input, the EDT is rerouted to the
501 swing event processing to handle further events.
502 </p>
503 <p>
504 The following diagram shows how this is used in <em>Spin</em>.
505 Each invocation of a bean's method is intercepted and handled by a
506 <a href="./api/spin/off/SpinOffEvaluator.html">SpinOffEvaluator</a>:</br>
507 getValue() is evaluated asynchronously on another thread (customizable with
508 a <a href="./api/spin/off/Starter.html">Starter</a>) while Swing events are
509 dispatched through a <a href="./api/spin/off/Dispatcher.html">Dispatcher</a>.
510 Once the call to the bean returns the dispatching of events is stopped and the
511 EDT is free to return to the standard event processing:
512 </p>
513 <p align="center">
514 <img src="sequencespinoff.gif" alt="Spin off sequence"/>
515 </p>
516 <p>
517 For asynchronous notifications from the bean to the GUI we reuse the technique
518 introduced in the previous sections.
519 But this time the call to <code>invokeAndWait()</code> is encapsulated by
520 <em>Spin</em> with a <a href="./api/spin/over/SpinOverEvaluator.html">SpinOverEvaluator</a>:
521 </p>
522 <p align="center">
523 <img src="sequencespinover.gif" alt="Spin over sequence"/>
524 </p>
525 <p>
526 Please take a look at the full <a href="./api/index.html">API</a> for details
527 on how to customize <em>Spin</em>.
528 </p>
529
530 <h2>
531 Utilizing CGLib
532 </h2>
533 <p>
534 <a name="cglib" />
535 Starting with release 1.4 Spin isn't any longer restricted on using JDK virtual
536 proxies. The creation of proxies is now encapsulated in the interface
537 <a href="./api/spin/ProxyFactory.html">ProxyFactory</a>.
538 </p>
539 <p>
540 <em>Spin</em> contains a <a href="http://cglib.sourceforge.net">CGLib</a> specific implementation
541 <a href="./api/spin/CGLibProxyFactory.html">CGLibProxyFactory</a> that offers the
542 following benefits:
543 <ul>
544 <li>
545 improved performance on interception of method invocations
546 </li>
547 <li>
548 no need to to use interfaces for your beans as required by JDK proxies
549 </li>
550 </ul>
551 For this you just have to change the default factory of proxies:
552 </p>
553 <pre>
554 Spin.setDefaultProxyFactory(new CGLibProxyFactory());
555 </pre>
556
557 <h2>
558 Caveats
559 </h2>
560 <p>
561 Although <em>Spin</em> handles threading transparently there are caveats with
562 <em>spin-off</em> that you should be aware of:
563 </p>
564 <dl>
565 <dt>Security</dt>
566 <dd>
567 For dispatching <em>Spin</em> needs access to AWT internals that are not
568 available in applets or untrusted JavaWebStart applications due to
569 security restrictions. This will hopefully change once AWT offers an
570 official way to dispatch events.<br/>
571 Meanwhile <em>Spin</em> offers alternative solutions which are less
572 performant but also work in a secured environment. Please take a look at
573 <a href="./api/spin/off/DialogDispatcherFactory.html">
574 DialogDispatcherFactory</a> and
575 <a href="./api/spin/off/InternalOptionPaneDispatcherFactory.html">
576 InternalOptionPaneDispatcherFactory</a>.
577 </dd>
578 <dt>Reference backdoor</dt>
579 <dd>
580 If your GUI hands over references to parts of its swing models
581 (e.g. TreeModel, TableModel) in method calls to your bean, these could
582 possibly be altered on another thread than the EDT thus VIOLATING THE
583 SWING SINGLE THREADING RULE.
584 </dd>
585 <dt>Bean threadsafety</dt>
586 <dd>
587 If your GUI doesn't disable all further actions while an invocation on
588 your bean is being processed, the event dispatching may cause a second
589 concurrent call to the bean. In cases where this is desired the BEAN MUST
590 BE THREADSAFE.
591 </dd>
592 <dt>Asynchronous<a name="asynchronous"/></dt>
593 <dd>
594 Whenever your GUI calls a beans method through <em>Spin</em>, further
595 actions should be allowed only if they are related to the current
596 <em>Spin</em> invocation. This includes <code>Cancel</code> functionality
597 and the retrieval of the current state of invocation or intermediate
598 results (e.g. for updating a progress bar or incremental filling of a
599 table).<br/>
600 You're running into problems if you're using <em>Spin</em> for real
601 asynchronous executions. Let me give an example:<br/>
602 File tranfers of an Explorer-like application wich can be arbitrarily
603 started and stopped while others are independently continuing are NOT A
604 RECOMMENDED USAGE for <em>Spin</em>. Nevertheless <em>Spin</em> can be
605 used to <em>spin-over</em> events from the transfers (e.g. completion
606 notification) to the EDT.
607 </dd>
608 <dt>Incomplete Event Handling</dt>
609 <dd>
610 An event that triggers <em>Spin</em> will not be completely precessed until
611 the return of the <em>Spin</em> invocation.<br/>
612 This might lead to minor visual annoyances, e.g. a JComboBox that does not
613 close its popup or a JButton that stays depressed while <em>Spin</em> is
614 running. But this behaviour could also result in other unexpected behaviours
615 that you should be aware of.</br>
616 Because of this <em>Swing</em> developers have expressed their concern about
617 <em>Spin</em> and similar techniques, stating that '<em>Swing</em> is not
618 completely reentrant'.<br/>
619 While this may be true, the same objection could be brought forward against
620 any modal dialog or modal internal frame. If you're using these in your
621 application there is no reason to be afraid of <em>Spin</em>.
622 </dd>
623 </dl>
624
625 <h2>
626 Conclusion
627 </h2>
628 <p>
629 <em>Spin</em> is a small library that concentrates on offering a powerful
630 solution to build non-freezing Swing applications. <em>Spin</em> enforces good
631 application design by separating the GUI and non-visual components through
632 interfaces. If it is used wisely in an application framework, the GUI
633 programmers will never have to think about threads again.
634 </p>
635 <p>
636 <em>Spin</em> comes with several demonstration classes that show how to solve
637 formerly challenging Swing programming problems with ease:
638 </p>
639 <ul>
640 <li>spin.demo.SpinOffGUI - shows how to execute extensive calculations
641 without "freeze"</li>
642 <li>spin.demo.SpinOverGUI - demonstrates asynchronous event notification
643 without pain</li>
644 <li>spin.demo.exception.ExceptionGUI - proves the transparent exception
645 handling offered by Spin</li>
646 <li>spin.demo.pogress.PullGUI - shows how to handle visual progress while
647 extensive calculations are executed</li>
648 <li>spin.demo.pogress.PushGUI - uses asynchronous event notification to update
649 a progressbar</li>
650 <li>spin.demo.prompt.CallGUI - explains how to prompt the user between
651 multiple extensive calculations</li>
652 <li>spin.demo.prompt.CallbackGUI - prompts the user for input which is
653 triggered by callbacks from an extensive
654 calculation</li>
655 <li>spin.demo.async.AsyncGUI - starts asynchronous calculations transparently
656 through Spin</li>
657 <li>spin.demo.dispatcher.DispatcherGUI - test different dispatchers</li>
658 </ul>
659 <p>
660 We have used <em>Spin</em> succesfullly in several projects to wrap all remote
661 communication (RMI) between rich-clients and the application server.
662 </p>
663
664 <h2>
665 Acknowledgments
666 </h2>
667 <p>
668 The <em>Spin</em> project is influenced by the
669 <a href="http://foxtrot.sourceforge.net">Foxtrot</a> project. Foxtrot is the
670 inventor of the <b>Synchronous Model</b> (<em>Spin</em> has adopted this
671 technique) but uses an API that is similar to <code>SwingWorker</code>.
672 It offers a subset of <em>Spin</em>s features - it lacks transparent exception
673 handling and offers no solution for asynchronous callbacks:
674 </p>
675 <pre>
676 public void actionPerformed(ActionEvent e)
677 {<span class="EDT">
678 label.setText("...");
679 String text = (String)Worker.post(new Job()</span>
680 {
681 public Object run()
682 {
683 <span class="notEDT">return bean.getValue();</span>
684 }
685 }<span class="EDT">);
686 label.setText(text);</span>
687 }
688 </pre>
689 <p>
690 The following code shows how Foxtrot can be 'simulated' with <em>Spin</em>
691 (if you insist on restricting yourself to only one generic interface named
692 <code>Job</code> or what-ever-you-like):
693 </p>
694 <pre>
695 public void actionPerformed(ActionEvent e)
696 {<span class="EDT">
697 label.setText("...");
698 String text = ((Job)Spin.off(new Job()</span>
699 {
700 public String run()
701 {
702 <span class="notEDT">return bean.getValue();</span>
703 }
704 }<span class="EDT">)).run();
705 label.setText(text);</span>
706 }
707
708 </pre>
709 </body>
710
711 </html>

  ViewVC Help
Powered by ViewVC 1.1.26