tag:blogger.com,1999:blog-75965163897412821762024-03-19T04:23:17.989+01:00Endre's Tech Corner!Tech stuff I likeEndre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.comBlogger24125tag:blogger.com,1999:blog-7596516389741282176.post-43646908831432443202012-12-01T20:08:00.000+01:002012-12-01T21:05:42.115+01:00Shell: Summing up lots of (large) numbersSometimes you want to know the exact amount of bytes all the files in a directory tree takes. For example, checking the sum of sizes of all files is a quick way to see if a copy operation went OK - if they are the same, there are reasons to believe that it is OK.<br />
<br />
<h3>
du gives varying numbers from filesystem to filesystem</h3>
However, 'du' doesn't always give the right number - is it the size of the directory nodes causing differences?
<br />
<pre class="brush: bash; toolbar: false;">du -sb .</pre>
<br />
<h3>
awk-solutions have major precision problems</h3>
The solutions flying across the intertubes using awk, goes awry with large numbers. The following have a ceiling on 2147483647, as that is the max of a 32 bit integer number. Absurdly, awk just displays that if it reaches the limit.
<br />
<pre class="brush: bash; toolbar: false;">find . -type f -printf "%s \n" | awk '{sum += $1 } END {printf "%i bytes\n",sum }'</pre>
<br />
You can get around that by going to floats (<a href="http://www.delorie.com/gnu/docs/gawk/gawk_260.html">awk really uses double-precision floating-point for all numbers</a>), but then you loose the whole point, exactness:
<br />
<pre class="brush: bash; toolbar: false;">find . -type f -printf "%s \n" | awk '{sum += $1 } END { printf "%.1f bytes\n",sum }'</pre>
<br />
<h3>
The Solution: bc - arbitrary precision calculator language!</h3>
Finding the sum of all the files in a directory tree:<br />
<pre class="brush: bash; toolbar: false;">echo `find . -type f -printf "%s+"`0 | bc
</pre>
<br />
Same in GiB (GigaBytes as in <i>1024^3</i> bytes), using absurd scale to get exactness ('scale' is bc's concept of decimal precision) :
<br />
<pre class="brush: bash; toolbar: false;">echo scale=50\; \(`find . -type f -printf "%s+"`'0)/(1024^3)' | bc
</pre>
<br />
If you want MiB or KiB, then change the <code>^3</code> to <code>^2</code> or <code>^1</code> respectively.Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com8tag:blogger.com,1999:blog-7596516389741282176.post-79728374105359447062012-10-04T10:42:00.000+02:002012-10-04T11:54:55.905+02:00KD-101 smoke detector batteriesI have a six-unit setup of KD-101 Smoke Detectors - they are conveniently linked together via radio.These are OEM units, and thus come packaged in utterly different packages, one, two or three in a package, and can be bought at very different price points (I've found them for 3 for 200 NOK, to 1 for 300!!), so shop around.<br />
<br />
Each unit uses both a 9 volt battery and three AA (LR03) 1,5 volt batteries. I wondered what the different batteries were used for; Apparently the unit functions just fine with only the 9v battery - it both runs the Test OK, and it triggers the other alarms just fine. However, it apparently cannot <i>receive</i> a trigger from the other alarms just on the 9v battery - <b>so apparently, the 3 x 1,5v batteries are used to run the receiver radio.</b><br />
<b><br /></b>
In my (unsuccessful) search of this answer found some instruction manuals, a good one is <a href="http://www.lmbrannservice.no/Customers/lmbrann/documents/Pdf%20okt%2009/Bruksanvisning_Nexa_KD_101LA.pdf">here</a> (PDF, Norwegian), and another one <a href="http://supply.se/files/manual/EL1313_Manual_100408.pdf">here</a> (PDF). None told what the different batteries were for, so that's the reason for this post.<br />
<br />
When the alarm beeps once every 45 seconds, the 3x AA batteries is to be changed.<br />
When it beeps once every 60 seconds, the 9v battery must be changed. (This difference in beep-timing is according to the Norwegian manual)<br />
<br />
I just changed all batteries to use Lithium cells. These typically last 10 years in a smoke detector, and the detector states that it should be exchanged after about 8 years, so I guess we're talking "life-time batteries" then! It cost just short of a 1000 kroners to buy 6 9v and 4x4 1,5 AA batteries - I've never been in the vicinity of paying that much for batteries before - not even car and boat batteries are than expensive! And it's more than I paid for the detectors themselves..!Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com7tag:blogger.com,1999:blog-7596516389741282176.post-82329593110852207722010-05-05T17:30:00.004+02:002012-10-04T11:19:17.292+02:00Linux Java Repeats RELEASED KeyEventsIn Java on Linux, there is <a href="http://bugs.sun.com/view_bug.do?bug_id=4153069">a 12 year old bug</a> in handling of keyboard auto-repeat. As on Windows, both KEY_PRESSED and KEY_TYPED repeats, but on Linux, also KEY_RELEASED repeats, while on Windows the released-event comes only when the user releases the key.<br />
<br />
This class can be installed as an AWTEventListener, and will seemingly fix this. Note that the class can be installed on both Windows and Linux - it won't affect already correct behavior.<br />
<br />
<pre class="brush: java">
package com.example;
import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.HashMap;
import java.util.Map;
import javax.swing.Timer;
/**
* This {@link AWTEventListener} tries to work around <a href="http://bugs.sun.com/view_bug.do?bug_id=4153069">a 12 yo
* bug</a> in the Linux KeyEvent handling for keyboard repeat. Linux apparently implements repeating keypresses by
* repeating both the {@link KeyEvent#KEY_PRESSED} <b>and</b> {@link KeyEvent#KEY_RELEASED}, while on Windows, one only
* gets repeating PRESSES, and then a final RELEASE when the key is released. The Windows way is obviously much more
* useful, as one then can easily distinguish between a user holding a key pressed, and a user hammering away on the
* key.
*
* This class is an {@link AWTEventListener} that should be installed as the application's first ever
* {@link AWTEventListener} using the following code, but it is simpler to invoke {@link #install() install(new
* instance)}:
*
* <br />
<pre>
* Toolkit.getDefaultToolkit().addAWTEventListener(new {@link RepeatingReleasedEventsFixer}, AWTEvent.KEY_EVENT_MASK);
* </pre>
*
* Remember to remove it and any other installed {@link AWTEventListener} if your application have some "reboot"
* functionality that can potentially install it again - or else you'll end up with multiple instances, which isn't too
* hot.
*
* <b>Notice:</b> Read up on the {@link Reposted} interface if you have other AWTEventListeners that resends KeyEvents
* (as this one does) - or else we'll get the event back.
* <br />
<h3>
Mode of operation</h3>
* The class makes use of the fact that the subsequent PRESSED event comes right after the RELEASED event - one thus
* have a sequence like this:
*
* <pre>
* PRESSED
* -wait between key repeats-
* RELEASED
* PRESSED
* -wait between key repeats-
* RELEASED
* PRESSED
* etc.
* </pre>
*
* A timer is started when receiving a RELEASED event, and if a PRESSED comes soon afterwards, the RELEASED is dropped
* (consumed) - while if the timer times out, the event is reposted and thus becomes the final, wanted RELEASED that
* denotes that the key actually was released.
*
* Inspired by <a href="http://www.arco.in-berlin.de/keyevent.html">http://www.arco.in-berlin.de/keyevent.html</a>
*
* @author <a href="http://endre.stolsvik.com/">Endre Stølsvik</a>
*/
public class RepeatingReleasedEventsFixer implements AWTEventListener {
private final Map<integer releasedaction="releasedaction"> _map = new HashMap<integer releasedaction="releasedaction">();
public void install() {
Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK);
}
public void remove() {
Toolkit.getDefaultToolkit().removeAWTEventListener(this);
}
@Override
public void eventDispatched(AWTEvent event) {
assert event instanceof KeyEvent : "Shall only listen to KeyEvents, so no other events shall come here";
assert assertEDT(); // REMEMBER THAT THIS IS SINGLE THREADED, so no need for synch.
// ?: Is this one of our synthetic RELEASED events?
if (event instanceof Reposted) {
// -> Yes, so we shalln't process it again.
return;
}
// ?: KEY_TYPED event? (We're only interested in KEY_PRESSED and KEY_RELEASED).
if (event.getID() == KeyEvent.KEY_TYPED) {
// -> Yes, TYPED, don't process.
return;
}
final KeyEvent keyEvent = (KeyEvent) event;
// ?: Is this already consumed?
// (Note how events are passed on to all AWTEventListeners even though a previous one consumed it)
if (keyEvent.isConsumed()) {
return;
}
// ?: Is this RELEASED? (the problem we're trying to fix!)
if (keyEvent.getID() == KeyEvent.KEY_RELEASED) {
// -> Yes, so stick in wait
/**
* Really just wait until "immediately", as the point is that the subsequent PRESSED shall already have been
* posted on the event queue, and shall thus be the direct next event no matter which events are posted
* afterwards. The code with the ReleasedAction handles if the Timer thread actually fires the action due to
* lags, by cancelling the action itself upon the PRESSED.
*/
final Timer timer = new Timer(2, null);
ReleasedAction action = new ReleasedAction(keyEvent, timer);
timer.addActionListener(action);
timer.start();
_map.put(Integer.valueOf(keyEvent.getKeyCode()), action);
// Consume the original
keyEvent.consume();
}
else if (keyEvent.getID() == KeyEvent.KEY_PRESSED) {
// Remember that this is single threaded (EDT), so we can't have races.
ReleasedAction action = _map.remove(Integer.valueOf(keyEvent.getKeyCode()));
// ?: Do we have a corresponding RELEASED waiting?
if (action != null) {
// -> Yes, so dump it
action.cancel();
}
// System.out.println("PRESSED: [" + keyEvent + "]");
}
else {
throw new AssertionError("All IDs should be covered.");
}
}
/**
* The ActionListener that posts the RELEASED {@link RepostedKeyEvent} if the {@link Timer} times out (and hence the
* repeat-action was over).
*/
private class ReleasedAction implements ActionListener {
private final KeyEvent _originalKeyEvent;
private Timer _timer;
ReleasedAction(KeyEvent originalReleased, Timer timer) {
_timer = timer;
_originalKeyEvent = originalReleased;
}
void cancel() {
assert assertEDT();
_timer.stop();
_timer = null;
_map.remove(Integer.valueOf(_originalKeyEvent.getKeyCode()));
}
@Override
public void actionPerformed(@SuppressWarnings ("unused") ActionEvent e) {
assert assertEDT();
// ?: Are we already cancelled?
// (Judging by Timer and TimerQueue code, we can theoretically be raced to be posted onto EDT by TimerQueue,
// due to some lag, unfair scheduling)
if (_timer == null) {
// -> Yes, so don't post the new RELEASED event.
return;
}
// Stop Timer and clean.
cancel();
// Creating new KeyEvent (we've consumed the original).
KeyEvent newEvent = new RepostedKeyEvent((Component) _originalKeyEvent.getSource(),
_originalKeyEvent.getID(), _originalKeyEvent.getWhen(), _originalKeyEvent.getModifiers(),
_originalKeyEvent.getKeyCode(), _originalKeyEvent.getKeyChar(), _originalKeyEvent.getKeyLocation());
// Posting to EventQueue.
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(newEvent);
// System.out.println("Posted synthetic RELEASED [" + newEvent + "].");
}
}
/**
* Marker interface that denotes that the {@link KeyEvent} in question is reposted from some
* {@link AWTEventListener}, including this. It denotes that the event shall not be "hack processed" by this class
* again. (The problem is that it is not possible to state "inject this event from this point in the pipeline" - one
* have to inject it to the event queue directly, thus it will come through this {@link AWTEventListener} too.
*/
public interface Reposted {
// marker
}
/**
* Dead simple extension of {@link KeyEvent} that implements {@link Reposted}.
*/
public static class RepostedKeyEvent extends KeyEvent implements Reposted {
public RepostedKeyEvent(@SuppressWarnings ("hiding") Component source, @SuppressWarnings ("hiding") int id,
long when, int modifiers, int keyCode, char keyChar, int keyLocation) {
super(source, id, when, modifiers, keyCode, keyChar, keyLocation);
}
}
private static boolean assertEDT() {
if (!EventQueue.isDispatchThread()) {
throw new AssertionError("Not EDT, but [" + Thread.currentThread() + "].");
}
return true;
}
}
package com.example;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
/**
* Tester for {@link RepeatingReleasedEventsFixer}.
*
* @author <a href="http://endre.stolsvik.com/">Endre Stølsvik</a>
*/
public class XRepeatingReleasedEventsFixer {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
startGUI();
}
});
}
private static void startGUI() {
new RepeatingReleasedEventsFixer().install();
JFrame frame = new JFrame("TestFrame");
JPanel main = new JPanel(new FlowLayout());
JButton listenedButton = new JButton("Have KeyListener");
main.add(listenedButton);
listenedButton.addKeyListener(new KeyListener() {
@Override
public void keyPressed(KeyEvent e) {
System.out.println("keyPressed: [" + e + "].");
}
@Override
public void keyTyped(KeyEvent e) {
System.out.println("keyTyped: [" + e + "].");
}
@Override
public void keyReleased(KeyEvent e) {
System.out.println("keyReleased: [" + e + "].");
}
});
main.add(new JButton("No Listeners"));
main.add(new JLabel("Try arrows, Ctrl, and chars,"));
main.add(new JLabel("as well as multiple at once."));
frame.add(main);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(260, 140);
frame.setVisible(true);
}
}</integer></integer><br />
<br />
</pre>
Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com11tag:blogger.com,1999:blog-7596516389741282176.post-13115963104202914372010-03-01T12:58:00.000+01:002010-03-01T12:58:04.452+01:00Blog This! : Communicate - Google Chrome Help<div>Testing, 1, 2, 3 - Google's Blog This!</div><div><br /></div><div>There's <a href="http://www.google.com/support/chrome/bin/static.py?page=guide.cs&guide=27542&topic=27573">a bunch of official Google Chrome extensions for Google products</a> - and this post is created using ..</div><div><br /></div><a href="http://www.google.com/support/chrome/bin/static.py?page=guide.cs&guide=27542&topic=27543&answer=173368">Blog This! : Communicate - Google Chrome Help</a>: "Post to Blogger with just one click"<br /><br />This extension adds a Blog This! button to the toolbar that you can click to create a new Blogger post. The new post is pre-populated with a link to the web page you're on, as well as any text you've highlighted on that page. Edit the post to your liking and post it instantly to your blog! Learn more about Blogger<br /><br />To learn more about the extension, visit its homepage in the Extensions Gallery. You can discover even more extensions in the gallery.Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com9tag:blogger.com,1999:blog-7596516389741282176.post-58644771674800813422010-02-18T01:42:00.006+01:002010-02-18T03:47:19.247+01:00JTextArea vs. Tab focus cycleWhen inside a JTextArea, the Tab key inserts a tab character instead of going to the next field. The reason is obvious - but in several situations it is not desirable, in particular when it would not make sense to insert tab characters, for example in a text area meant for editing keywords.<br /><br />Lets just spill the solution right away:<pre>JTextArea text = new JTextArea();<br />text.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, null);<br />text.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, null);<br /></pre><br />There is also a built-in solution, which is that Ctrl-[Shift-]Tab still works. Actually, those keys always work. Those keys are also the solution when you suddenly find yourself focus-stuck within a JTable. I first thought that a JTable constituted a a "focus cycle root", but it doesn't - it is just the same effect as with the JTextArea problem here outlined (and can be "fixed" the same way).<br /><br />However, Ctrl-Tag does not quite give the feeling one typically is after, and in particular not when one use the JTextArea more as a solution to text that typically is less than a line big, but can become multiline. Such a field thus looks like a JTextField, but the Tab character suddenly have a completely different meaning.<br /><br />I Googled a lot, and there are lots of solutions (or rather hacks), but didn't find the above solution, which seems to be the most straightforward after Java 1.5. The huge heap of questions and solutions should, IMHO, give a hint to the Swing team that a specific method for a JTextArea to not suck up Tab events should be made available.<br /><br />The <code>null</code> means that it will take the focus keys from its parent.Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com5tag:blogger.com,1999:blog-7596516389741282176.post-27186804215878353082010-02-11T16:21:00.006+01:002010-02-11T16:53:34.706+01:00Anonymous class implementing interfaceWhy is this not allowed:<blockquote><div><span class="Apple-style-span" style="font-family:'courier new';">JPanel panel = new JPanel(...) <i><b>implements Scrollable</b></i> {</span></div><div><span class="Apple-style-span" style="font-family:'courier new';"> ...</span></div><div><span class="Apple-style-span" style="font-family:'courier new';">};</span><div><blockquote><div></div></blockquote><div></div></div></div></blockquote><div><div><div><br /></div><div>I've ended up trying to write something like that several times - and each time I start to wonder what is wrong! A problem is of course that you could not directly access the methods of the implemented interface, but you could still have <span class="Apple-style-span" style="font-family:'courier new';">instanceof</span>'ed and <span class="Apple-style-span" style="font-family:'courier new';">cast</span>'ed it. Some interfaces are also just used for marking, e.g. Cloneable.</div><div><br /></div><div>It is allowed with a local class:</div><div><div><span class="Apple-style-span" style="font-family:'courier new';"></span></div><blockquote><div><span class="Apple-style-span" style="font-family:'courier new';">class ScrollableJPanel extends JPanel implements Scrollable {</span></div><div><span class="Apple-style-span" style="font-family:'courier new';"> ScrollableJPanel() {</span></div><div><span class="Apple-style-span" style="font-family:'courier new';"> super(...);</span></div><div><span class="Apple-style-span" style="font-family:'courier new';"> }</span></div><div><span class="Apple-style-span" style="font-family:'courier new';"> ...</span></div><div><span class="Apple-style-span" style="font-family:'courier new';">}</span></div><div><span class="Apple-style-span" style="font-family:'courier new';"></span></div><div><span class="Apple-style-span" style="font-family:'courier new';">JPanel panel = new ScrollableJPanel();</span></div><div></div></blockquote><div>The restriction seems arbitrary.</div><div><br /></div><div>PS: Oh my god Blogger sucks. I have GOT to get away from this crappy blogging software. How is it possible to not have made any progress the last 5 years?!</div></div></div></div>Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com3tag:blogger.com,1999:blog-7596516389741282176.post-56410072714330659792010-01-30T21:16:00.017+01:002010-01-31T04:19:14.261+01:00Linux Java Thread Priorities workaroundFirst and foremost: This workaround was found by <a href="http://www.akshaal.info/">Akshaal</a>, and his blogentry about the problem and the "fix" is <a href="http://www.akshaal.info/2008/04/javas-thread-priorities-in-linux.html">here</a> (2008-04-28).<div><br /></div><div>Secondly: Fix/Workaround to let Java on Linux work somewhat with Thread priorities, start the JVM with the following argument:</div><div><span class="Apple-style-span" style=" ;font-family:'courier new';">-XX:ThreadPriorityPolicy=42</span></div><span class="Apple-style-span" style="font-family:'courier new';"><span class="Apple-style-span" style=" ;font-family:Georgia, serif;"><div><br /></div><div>For some annoying reason, Sun has decided that to run with threads with working thread priorities on Linux, you have to be root. The logic behind this decision, is that to <i>heighten</i> thread priorities, you have to be root. But, says many of us, could you not just let us lower thread priorities, at least? No, says Sun. I believe they just don't quite understand what is requested.</div><div><br /></div><div>The sun <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4813310">bug 4813310</a> tracks this, but it is Closed - Fix Delivered. So the chances of a fix are slim, unless some JDK developer takes mercy.</div><div><br /></div><div>Anyway - Akshaal found out that if you just set the "ThreadPriorityPolicy" to something else than the legal values 0 or 1, say 2 or 42, or 666 as he suggest himself, a slight logic bug in Sun's JVM code kicks in, and thus sets the policy to be as if running with root - thus you get exactly what one desire. The operating system, Linux, won't allow priorities to be heightened above "Normal" (negative nice value), and thus just ignores those requests (setting it to normal instead, nice value 0) - but it lets through the requests to set it lower (setting the nice value to some positive value).</div><div><br /></div><div>I wrote a little program to test out priorities and these flags. There are two obvious ways to test such scheduling: Either have a fixed amount of work for each thread at each priority, and time how long time it takes to execute this - or let the system run for a fixed time, and then count how much work is done during this time. I first thought of the latter, so I coded that - and as I think about it further, I realize that this must be the most correct check too: Since if the high priority jobs finish earlier, the timing data of the lower priority jobs will not be entirely correct: There are now, obviously, fewer threads competing for the CPU (of course, one could let each thread do X work, then note how long time that took - and then have the same thread do unlimited amount of work afterward, so that it didn't skew the measurements by exiting before the least priority thread was finished).</div><div><br /></div><div><br /></div><div>Here are the results (2010-01-29 on JDK 1.6.0_18):</div><div><br />As user, with the following arguments (for the rest of the runs, the things changing are user vs. root, and the ThreadPriorityPolicy):<br /><br />-XX:ThreadPriorityPolicy=0<br />-XX:+PrintGCDetails<br />-XX:+PrintGCTimeStamps<br />-XX:+PrintCompilation<br /><pre> 1 java.util.concurrent.atomic.AtomicLong::get (5 bytes)<br />--- n sun.misc.Unsafe::compareAndSwapLong<br />--- n sun.misc.Unsafe::compareAndSwapLong<br /> 2 java.util.concurrent.atomic.AtomicLong::compareAndSet (13 bytes)<br /> 3 java.util.Random::next (47 bytes)<br /> 4 java.util.Random::nextDouble (24 bytes)<br /> 1% com.example.XThreadPriorities$Runner::run @ 13 (70 bytes)<br /> 5 com.example.XThreadPriorities$Runner::run (70 bytes)<br />Warmup complete.<br />Running test.<br />Thread MIN_PRIORITY:[1], NORM_PRIORITY:[5], MAX_PRIORITY:[10].<br />3924841464 - Pri:1 - 448333661 451444766 472740484 413017157 417892801 548408461 605271612 567732522<br />3948617160 - Pri:2 - 592733363 404123318 425058572 604107501 519520686 418888175 526486630 457698915<br />4266211603 - Pri:3 - 761499319 427767980 429864266 603262254 560988584 418244133 641052397 423532670<br />3646125561 - Pri:4 - 404603024 416434536 429997217 431405433 641421096 478439739 420713663 423110853<br />3968532194 - Pri:5 - 415064763 422632368 419370153 604712758 561722145 418841260 521415092 604773655<br />4006791532 - Pri:6 - 420221100 422553508 614947805 561412057 641625815 479341536 432081606 434608105<br />4014589807 - Pri:7 - 585756485 418316991 520996504 474136186 637166908 419697931 434488836 524029966<br />4192942528 - Pri:8 - 480067579 472195975 589334074 519719204 640510132 420580090 462976949 607558525<br />3965592952 - Pri:9 - 419045425 475155351 473396403 540690307 520495432 522822449 598591571 415396014<br />3603044034 - Pri:10 - 419117989 421450664 520872436 429775340 566700552 418128806 414890139 412108108<br />TotalyDummy: [0.13069701389705252].<br />Heap<br /> PSYoungGen total 27904K, used 957K [0x00000000e0e00000, 0x00000000e2d20000, 0x0000000100000000)<br /> eden space 23936K, 4% used [0x00000000e0e00000,0x00000000e0eef640,0x00000000e2560000)<br /> from space 3968K, 0% used [0x00000000e2940000,0x00000000e2940000,0x00000000e2d20000)<br /> to space 3968K, 0% used [0x00000000e2560000,0x00000000e2560000,0x00000000e2940000)<br /> PSOldGen total 63744K, used 0K [0x00000000a2a00000, 0x00000000a6840000, 0x00000000e0e00000)<br /> object space 63744K, 0% used [0x00000000a2a00000,0x00000000a2a00000,0x00000000a6840000)<br /> PSPermGen total 21248K, used 2534K [0x000000009d600000, 0x000000009eac0000, 0x00000000a2a00000)<br /> object space 21248K, 11% used [0x000000009d600000,0x000000009d8799c8,0x000000009eac0000)<br /></pre><br />As user, with -XX:ThreadPriorityPolicy=1. Notice the warning:<br /><br /><b><code>Java HotSpot(TM) 64-Bit Server VM warning: -XX:ThreadPriorityPolicy requires root privilege on Linux</code></b><br /><pre>...<br />4074355867 - Pri:1 - 527880034 510291794 506778321 500873083 507354362 494273347 543124483 483780443<br />4070970485 - Pri:2 - 502093074 507450387 513538019 548011008 515777472 510318658 492394477 481387390<br />4166089696 - Pri:3 - 545072864 510212654 503618003 545454609 509985968 499563619 542154550 510027429<br />3938795103 - Pri:4 - 499309103 482262429 483892136 484047646 493485804 497920357 486252326 511625302<br />4030810973 - Pri:5 - 492208426 510091021 472609917 551737964 511926521 488736661 495151696 508348767<br />3991986357 - Pri:6 - 499139701 484193553 508453245 494823573 505428815 491609383 495135614 513202473<br />3970332529 - Pri:7 - 508806026 484872150 460815795 545766805 487437289 484429061 511512703 486692700<br />3985129481 - Pri:8 - 508044296 493994479 486757819 504809264 504582073 489230988 485472873 512237689<br />3993531405 - Pri:9 - 485761906 508066620 498470220 526708671 484472110 474413825 498665020 516973033<br />3880591568 - Pri:10 - 482605449 492966477 494274413 512614969 525299924 401406096 483149581 488274659<br /></pre><br /><br />As root, with -XX:ThreadPriorityPolicy=1:<br /><pre>...<br />1192628609 - Pri:1 - 146366375 145886054 145439504 148594629 154873659 145337314 155007218 151123856<br />1600638473 - Pri:2 - 293845127 182254047 179729049 186290117 174455802 186991284 194082370 202990677<br />1865691639 - Pri:3 - 252269194 240399347 219981901 223897910 228422404 242806673 239024804 218889406<br />2405100112 - Pri:4 - 303976233 283413460 310125261 300915820 286445060 313188957 295836808 311198513<br />2895152789 - Pri:5 - 355221596 377247451 374614383 338382343 361193338 352556243 376620158 359317277<br />3575027949 - Pri:6 - 430714357 452538422 438269143 451133734 476982979 437064495 436451954 451872865<br />4443379518 - Pri:7 - 576373599 575729812 528069437 559187021 582029237 539218400 520491072 562280940<br />5573457006 - Pri:8 - 684559763 685997740 722733003 672837267 683740096 716802117 697600305 709186715<br />7105117744 - Pri:9 - 885538364 920253981 855196886 825353759 909130110 960094293 864861817 884688534<br />9047448786 - Pri:10 - 1102826483 1079059728 1170567845 1062711472 1103250654 1194296507 1152074721 1182661376<br /></pre><br /><br />As user, with "hack" -XX:ThreadPriorityPolicy=42:<br /><pre>...<br />1908592461 - Pri:1 - 252347610 268364950 217692707 199375572 241284341 243700244 267888904 217938133<br />2553635695 - Pri:2 - 345500976 320245701 313000117 330487363 307702106 304209714 312839609 319650109<br />3061308188 - Pri:3 - 352952639 431153057 415115944 336767623 412447023 393943268 380318370 338610264<br />4023325349 - Pri:4 - 532822164 500246668 509185449 515950251 422229930 551608063 511732302 479550522<br />4789209609 - Pri:5 - 625601672 530227747 622740566 599139605 725003140 602205329 596298732 487992818<br />4730674281 - Pri:6 - 625476539 482470376 726537575 655716492 487662808 621938212 601082560 529789719<br />4881113871 - Pri:7 - 652977961 586461749 726919389 595618652 484349203 661139324 684148172 489499421<br />4533403095 - Pri:8 - 614634085 484084154 526523754 643123946 599005063 647009071 532566904 486456118<br />4727219915 - Pri:9 - 625026801 596318117 726547378 598978337 601971731 489284222 491219584 597873745<br />4827951033 - Pri:10 - 678619163 525617294 592417838 657638409 722179525 484460205 531506557 635512042<br /></pre><br /><br /><br />As user, with "hack" and specific setting of OS priorities. We start at Unix nice level 0 for the highest java priority, going down to 9 for the lowest java priority. <b>Notice how this leads to the situation where a NORMAL priority java thread have a quite significant nice level (low priority) on the OS side.</b> Thus, in a contended environment with other processes running on the machine competing for CPU resources, "normal" java threads will loose out to the other processes, so this is not a complete solution:<br /><br />-XX:ThreadPriorityPolicy=42<br />-XX:JavaPriority10_To_OSPriority=0<br />-XX:JavaPriority9_To_OSPriority=1<br />-XX:JavaPriority8_To_OSPriority=2<br />-XX:JavaPriority7_To_OSPriority=3<br />-XX:JavaPriority6_To_OSPriority=4<br />-XX:JavaPriority5_To_OSPriority=5<br />-XX:JavaPriority4_To_OSPriority=6<br />-XX:JavaPriority3_To_OSPriority=7<br />-XX:JavaPriority2_To_OSPriority=8<br />-XX:JavaPriority1_To_OSPriority=9<br /><pre>...<br />1181594447 - Pri:1 - 156646712 163727007 149271484 148003186 146046042 142171936 136636764 139091316<br />1467758385 - Pri:2 - 181741621 207823162 180440610 178282147 181600823 175446181 174811880 187611961<br />1835430827 - Pri:3 - 249361299 241085322 198475979 220310800 212862944 227538859 223721751 262073873<br />2310513758 - Pri:4 - 298831212 311762873 263149604 261915158 279594960 280329975 284289057 330640919<br />2820606635 - Pri:5 - 417528424 337381991 374884808 300077072 359420239 346286405 382398636 302629060<br />3517176583 - Pri:6 - 450919929 431990553 434093985 473421346 471673385 375806611 457967591 421303183<br />4386636298 - Pri:7 - 526555707 475575838 600301353 603713184 570267602 475066321 489751343 645404950<br />5646571395 - Pri:8 - 897622033 693133727 703208339 759090217 712003197 591393673 618879272 671240937<br />6260518137 - Pri:9 - 797127673 797467534 739969033 913730558 735004475 798095374 736360300 742763190<br />10144752105 - Pri:10 - 1504673551 1155125325 1123163868 1302155597 1390019512 1216611268 1208440124 1244562860<br /></pre><br />To directly see the problem where normal threads, which have java priority 5, gets unfair treatment with the above solution, I ran a quick test with two such tests simultaneously using the following little shell script (I also didn't run for 5 minutes here):<br /><pre>$ cat test.sh <br />#!/bin/sh<br /><br />java \<br /> -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintCompilation -classpath eclipsebuild com.example.XThreadPriorities &<br /><br />java \<br /> -XX:ThreadPriorityPolicy=42 \<br /> -XX:JavaPriority10_To_OSPriority=0 \<br /> -XX:JavaPriority9_To_OSPriority=1 \<br /> -XX:JavaPriority8_To_OSPriority=2 \<br /> -XX:JavaPriority7_To_OSPriority=3 \<br /> -XX:JavaPriority6_To_OSPriority=4 \<br /> -XX:JavaPriority5_To_OSPriority=5 \<br /> -XX:JavaPriority4_To_OSPriority=6 \<br /> -XX:JavaPriority3_To_OSPriority=7 \<br /> -XX:JavaPriority2_To_OSPriority=8 \<br /> -XX:JavaPriority1_To_OSPriority=9 \<br /> -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintCompilation -classpath eclipsebuild com.example.XThreadPriorities &<br /></pre><br />This lead to the following result:<br /><pre>70479632 - Pri:1 - 15781306 7639485 8364936 7772256 7424872 7930535 7750748 7815494<br />80416568 - Pri:2 - 11971141 9843537 9242104 9872974 10394428 9485233 9989330 9617821<br />103451737 - Pri:3 - 15886246 12949826 11725092 13793650 12614427 11656444 12344883 12481169<br />143220837 - Pri:4 - 32940102 16299182 14991785 16375282 15634409 14790916 15922255 16266906<br />159230735 - Pri:5 - 20471865 19681922 18727162 25211867 17791552 18787357 19537079 19021931<br />199052863 - Pri:6 - 24375945 25213341 24205803 31016513 22616403 23760056 23570549 24294253<br />245144874 - Pri:7 - 32218827 32270732 30006087 31789704 27064661 32474676 30256065 29064122<br />318656402 - Pri:8 - 38021055 39259080 44766347 49071457 34923402 35975708 40544736 36094617<br />401223243 - Pri:9 - 48600625 49467360 60141538 49625176 43789040 56507123 48418800 44673581<br />483842551 - Pri:10 - 58150216 58609942 77028357 59819032 53866188 59828368 59407580 57132868<br />TotalWork: [2204719442] (TotalyDummy: [29.703316307452084]).<br /><br />469978381 - Pri:1 - 78278984 54629208 68421300 50861462 59341367 47883726 52597471 57964863<br />446759823 - Pri:2 - 77297533 50977670 73351930 50608554 47847622 46878431 53587386 46210697<br />472936111 - Pri:3 - 59336726 53398323 72483088 72872361 62524544 50161550 54083565 48075954<br />460567710 - Pri:4 - 59351070 51692155 72606512 72652587 47014898 52273533 46462284 58514671<br />467930078 - Pri:5 - 58932415 51900432 71509566 73609344 59049535 51626557 46980275 54321954<br />416276726 - Pri:6 - 58922845 55446857 47075994 47381016 47463150 52581761 52572845 54832258<br />461001441 - Pri:7 - 69723843 59707979 47522091 53322726 47818110 67710544 60742003 54454145<br />455315271 - Pri:8 - 49172227 61493744 62323717 50959541 47825115 70684358 51816372 61040197<br />424012117 - Pri:9 - 49651798 56514868 46221793 54045253 53094738 52019573 55008343 57455751<br />428007514 - Pri:10 - 48523372 47959147 49071583 62248855 51265246 51405853 54465519 63067939<br />TotalWork: [4502785172] (TotalyDummy: [-14.989963287910708]).<br /></pre><br /><div><br />The best solution for running as user is probably the "-XX:ThreadPriorityPolicy=42" without the specific setting of OS levels. In that scenario, a normal priority java thread gets a normal OS nice level, while it still is possible to let e.g. "batch processing threads" get a lower nice level. The only thing that I'd appreciate was if the (AWT) Event Dispatch Thread got a notch higher priority than all other threads. Java does exactly that, by setting the EDT's priority to 6 (but as shown, that doesn't matter here, all priorities higher than 5 gets effectively 5 anyway). However, by setting all other threads in a GUI application down a notch (to 4 or less), you'd get the same effect.</div><div><br /></div><div>Just to point it out: The above solution should be the default. This gives at least a somewhat reasonable logic: You can lower the priority, but you cannot jack it above normal. What one really would want, is that these priorities only worked within the process, not globally across the system. Then one could jack some threads "above normal", as they would only affect the scheduling within this process' threads. But this would apparently require a change in Linux.</div><div><br /></div><div>Here's the code. It should be possible to just copy-paste it onto a project node of a Eclipse project (stand on the project node, hit Ctrl-V), thus getting it runnable instantly:</div><div><br /><pre>package com.example;<br /><br />import java.util.Random;<br /><br />/**<br /> * Tests the Thread priorities of a Java program by letting threads run "unlimited amounts" of work for a fixed period<br /> * of time, counting how much work they get done.<br /> * <br /> * @author <a href="http://endre.stolsvik.com/">Endre Stølsvik</a><br /> */<br />public class XThreadPriorities {<br /><br /> static volatile boolean _runThreads;<br /><br /> public static final int PARALLELS = Runtime.getRuntime().availableProcessors();<br /><br /> public static final int RUNNING_WARMUP_SECONDS = 4;<br /><br /> public static final int RUNNING_SECONDS = 60 * 5;<br /><br /> public static void main(String[] args) throws InterruptedException {<br /> // Run warmup<br /> run(RUNNING_WARMUP_SECONDS);<br /> System.out.println("Warmup complete.");<br /> Thread.sleep(500);<br /> System.out.println("Running test.");<br /> // Run test<br /> Runner[][] runners = run(RUNNING_SECONDS);<br /><br /> // Dump results.<br /> System.out.println("Thread MIN_PRIORITY:[" + Thread.MIN_PRIORITY + "], NORM_PRIORITY:[" + Thread.NORM_PRIORITY<br /> + "], MAX_PRIORITY:[" + Thread.MAX_PRIORITY + "].");<br /><br /> double totalDummy = 0;<br /> long totalWork = 0;<br /> for (int j = 0; j < runners[0].length; j++) {<br /> int pri = runners[0][j]._priority;<br /> String msg = "Pri:" + pri + " - ";<br /> long total = 0;<br /> for (int i = 0; i < runners.length; i++) {<br /> totalDummy = runners[i][j]._dummy;<br /> assert pri == runners[i][j]._priority;<br /> long counter = runners[i][j]._counter;<br /> total += counter;<br /> msg += " " + counter;<br /> }<br /> totalWork += total;<br /> System.out.println(total + " - " + msg);<br /> }<br /> System.out.println("TotalWork: [" + totalWork + "] (TotalyDummy: [" + totalDummy + "]).");<br /> }<br /><br /> private static Runner[][] run(int seconds) throws InterruptedException {<br /> _runThreads = true;<br /> int priorities = Thread.MAX_PRIORITY - Thread.MIN_PRIORITY + 1;<br /> Runner[][] runners = new Runner[PARALLELS][priorities];<br /> // Make threads<br /> for (int i = 0; i < PARALLELS; i++) {<br /> int pri = Thread.MIN_PRIORITY;<br /> for (int j = 0; j < priorities; j++) {<br /> runners[i][j] = new Runner(pri++);<br /> }<br /> }<br /> // Start threads.<br /> for (int i = 0; i < PARALLELS; i++) {<br /> for (int j = 0; j < priorities; j++) {<br /> runners[i][j].start();<br /> }<br /> }<br /> // Run all threads for whatever number of seconds.<br /> Thread.sleep(seconds * 1000);<br /> // Ask threads to stop.<br /> _runThreads = false;<br /> // Make sure they're stopped by joining them<br /> for (int i = 0; i < PARALLELS; i++) {<br /> for (int j = 0; j < priorities; j++) {<br /> runners[i][j].join();<br /> }<br /> }<br /> return runners;<br /> }<br /><br /> private static class Runner extends Thread {<br /> final Random _random = new Random();<br /> final int _priority;<br /><br /> public Runner(int priority) {<br /> this._priority = priority;<br /> }<br /><br /> long _counter = 0;<br /> double _dummy = 0;<br /><br /> @Override<br /> public void run() {<br /> Thread.currentThread().setPriority(_priority);<br /> while (_runThreads) {<br /> _counter++;<br /> _dummy += (_random.nextDouble() * 2 - 1) / 123.579 * (_random.nextDouble() * 2 - 1);<br /> }<br /> }<br /> }<br />}<br /></pre><br /></div><div><br /></div></span></span></div>Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com52tag:blogger.com,1999:blog-7596516389741282176.post-87466072902465673842010-01-09T04:21:00.010+01:002010-02-02T04:22:34.024+01:00Eclipse on Ubuntu Karmic behaves weirdlyI am installing my new machine these days, running Ubuntu Karmic Koala 9.10. I finally came to setting up my environment on Eclipse, going to install Subversive for SVN access.<div><br /></div><div>I then find that the <i>Install new software...</i> dialog behaves very weird - I can't seem to get anything up in the checkable list box when I select anything from the "Work with:" site selector dropdown.</div><div><br /></div><div>Also, I find that I very often am unable to click buttons on any dialog window - this also goes for the <i>Preferences</i> dialog (it is possible to Tab around and hit enter, though).</div><div><br /></div><div>It turns out that both these problem is an incompatibility between SWT (the Standard Widget Toolkit that Eclipse uses) and GTK+ (The GIMP Toolkit, used by GNOME) v.2.18 and apparently also Compiz (The advanced compositing window manager that can be used with GNOME). The underlying problem <a href="https://bugs.launchpad.net/gtk/+bug/442078/comments/28">is apparently</a> that GTK has changed some internal way windows are handled that exposes not-quite-correct usage by some applications, notably SWT. So SWT have to fix it, which they have.</div><div><br /></div><div>However, a fix is not "out" yet, <b>but there is a workaround</b>, which is to run..</div><div><br /></div><div><code> export GDK_NATIVE_WINDOWS=true</code></div><div><br /></div><div>..before invoking eclipse (in a terminal, obviously). Some people apparently find that if you reboot eclipse (restart, or switch workspace), the fix doesn't seem to stick - however it seems to for me.</div><div><br /></div><div>Links to bugs: <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=291257">Eclipse</a>, <a href="https://bugs.launchpad.net/ubuntu/+source/gtk+2.0/+bug/442078">Ubuntu</a></div><div><br /></div><div>Btw, same thing goes for Azureus (and hence Vuze), as they also use SWT.</div><div><br /></div><div>Btw2, the same thing also goes for Flash in the browsers (typically one find this out by buttons in youtube videos not working!), but here google will have to be your friend.</div><div><br /></div><div>Btw3, to install <a href="http://www.eclipse.org/subversive/">Subversive</a> for subversion access, launch the <i>Install new software...</i> dialog, select the <i>Galileo site</i> from the dropdown, and just search for "subversive". Find and check <i>Subversive SVN Team Provider (Incubation)</i> under <i>Collaboration</i>. Also check the <i>Mylyn Integration</i> if applicable, then install. When you reboot, the Subversive plugin <a href="http://www.polarion.com/products/svn/subversive/connector_discovery.php">will find out</a> that you have not installed those annoying Subversive Connectors (the actual subversion handlers - which are not distributed from the eclipse main site due to some licensing bullshit) which you had to handle yourself in previous versions of eclipse - and let you select them from a nice GUI.</div>Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com1tag:blogger.com,1999:blog-7596516389741282176.post-5382091463904968802010-01-08T00:46:00.023+01:002010-01-24T17:19:15.164+01:003D cinema explained<a href="http://en.wikipedia.org/wiki/Avatar_(2009_film)">Avatar 3D</a> is amazing.<div><br /></div><div>Do you wonder how the 3D works? I did. Turns out it is distributed in several ways. Most cinemas use <a href="http://en.wikipedia.org/wiki/Dolby_3D">Dolby 3D</a> or <a href="http://en.wikipedia.org/wiki/RealD">RealD</a>. I got to see Avatar 3D in RealD, and it was nice!</div><div><br /></div><div>The most important requirement to achieve actual <a href="http://en.wikipedia.org/wiki/Depth_perception#Binocular_cues">depth perception</a> is to get a different (distinct) picture to the <a href="http://en.wikipedia.org/wiki/Binocular_vision">each of the left and right eye</a>, thus achieving <a href="http://en.wikipedia.org/wiki/Stereopsis">stereopsis</a>. This can be achieved in a variety of ways, but in cinema, the most obvious way (if not sole way, at this point in time), is to have the spectators wear glasses that separate two pictures that are displayed on top of each other on the same screen.</div><div><br /></div><div>One way is to use <a href="http://en.wikipedia.org/wiki/Anaglyph_image">colored glasses</a>. The really old-skool is to use red/green or red/blue. This ends up loosing out on the blue or green component. Thus, a newer way is thus to use red / green-blue (cyan). These separate color inputs is somewhat fused in the brain, thus giving "full colors", but the effect is rather annoying, and you can literally feel the tearing in your brain as it tries to restore the 3D information from the spatially slightly differing images (but which have massively different color contents) and at the same time fuse the colors, into one single full color 3D image.</div><div><br /></div><div><a href="http://www.dolby.com/professional/solutions/cinema/3d-digital-cinema.html">Dolby 3D</a> uses an extended version of this colored glasses idea: it lets both eyes see red, green and blue - only different variations of the colors. The glasses is thus very specific in the bands they let through: One band of red, one band of green and one band of blue for the left eye, and another band of red/green/blue for the right - without having an overlap. According to the WP article, "Difference in color perception for the left and right eye are corrected within the glasses through additional filters" - this just needs to correct the relative strength of the r/g/b on one eye, and average on the other, since the eye doesn't distinguish between any two versions of red as long as they don't affect the other colors. To avoid using two projectors, <a href="http://www.dolby.com/professional/solutions/cinema/3d-digital-cinema.html">a special color wheel</a> that alternates between these two sets of red/green/blues is installed, so that each eye sees alternatively a black frame, or the image dedicated to it. A benefit of this system is that the screen can be any white surface, as it does not depend on any polarization effect of the light. Also, head-tilting does not pose a problem.</div><div><br /></div><div><a href="http://www.reald.com/Content/cinema.aspx">RealD</a>, on the other hand, use <a href="http://en.wikipedia.org/wiki/Polarization_(waves)">polarized light</a>. This way of separation needs a literal <a href="http://en.wikipedia.org/wiki/Silver_screen">silver screen</a> - yes, it is not just a figure of speech, there was actual silver involved. And now, with the needs of polarization-based 3D cinema, there is again. The obvious idea here is to use vertical polarization on one eye and horizontal on the other. This idea have been employed many times throughout the history of cinema. However, this has a problem when the viewer tilts his head: Since both glasses now are out of alignment, both eyes will see both images, effectively killing the 3D effect as if not wearing the glasses at all. RealD have fixed this, and this initially baffled me when checking out the <a href="http://www.reald.com/Content/cinemaProducts.aspx?pageID=13">glasses</a> - by holding two sets of glasses over each other, one tilted 90 degrees, one could still see through them (try that with two sets of polarizing sunglasses!). They use <a href="http://en.wikipedia.org/wiki/Circular_polarization">circular polarization</a>. Again, to not have to install two projectors, a device in front of the projector switches between the two polarizations (apparently not using a rotating wheel, even though this should be possible since the polarization is circular instead of horizontal/vertical, in which case a rotating device would not be practical).</div><div><br /></div><div>Here's a <a href="http://science.howstuffworks.com/3-d-glasses.htm">"How stuff works" article</a> on 3D glasses.</div><div><br /></div><div>Here's a <a href="http://www.avsforum.com/avs-vb/showthread.php?s=4fdf384a5881999792f3aee48036a6a7&p=15388203#post15388203">good forum post</a> on this exact subject. He points out that 3D is not new in any way - "In the 20's, sound was a gimick. In the 30's, color was a gimick, in the 50's/60's 3D and widescreens where both gimicks. Widescreens caught on, 3D didn't."</div><div><br /></div><div>And <a href="http://news.cnet.com/8301-13580_3-9821709-39.html">here's an article</a> that compares the three techniques now in existence - <a href="http://en.wikipedia.org/wiki/IMAX_3D">IMAX 3D</a>, RealD and Dolby 3D - for the same movie, Beowulf. He goes into a bit of technical detail. (IMAX 3D uses vertical/horizontal polarization, but with two distinct projectors, currently employing actual film.)</div><div><br /></div><div>As an absurd aside, I happened across this article about some kind of shrimp, the <a href="http://en.wikipedia.org/wiki/Mantis_shrimp">Mantis shrimp</a>, that "sees" circularly polarized light. Other animals can distinguish between different linearly polarized light, which for example is nice when you have to see through a water surface (the water surface suddenly vanishes - try this with a SLR camera with a polarizing filter installed, or with your polarizing sunglasses, tilting your head to one side or the other, when standing ashore trying to see the bottom of the sea). However, these guys can distinguish any polarization from circular, through elliptic, to linear, in any phase. What benefit would this have for the shrimp? As a researcher pointed out: "Some of the animals that the [mantis shrimp] like to eat are transparent, and quite hard to see in sea-water – except that they're packed full of polarizing sugars – which makes them light up like Christmas trees as far as these shrimp are concerned". The Mantis shrimp seems like an awesome killer - they have <a href="http://en.wikipedia.org/wiki/Hyperspectral">hyperspectral vision</a> (vision that stretches out to ultraviolet and infrared), and can as mentioned distinguish any polarization, and have, depending on species, either built-in spears or clubs, which they can employ with an acceleration of 10,400 g and which acquire speeds of 23 m/s, about the acceleration of a .22 calibre bullet. This is so fast that cavitation bubbles form, which give a second shock - so if the guy misses you with its club, you might die from the shockwave from the collapsing cavitation bubbles! Nice that I don't have to be on the lookout for such dudes every day.</div>Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com2tag:blogger.com,1999:blog-7596516389741282176.post-32828224333876907702009-12-22T07:23:00.013+01:002009-12-22T10:26:00.147+01:00AVG Antivirus is a pest!AVG Antivirus is itself a pest in several regards.<br /><br />Coming home to mom and dad for Christmas, I find that their Firefox is eating at least 50% CPU (of this admittedly slow machine) when showing a blank page! So an already slow machine is now really dead slow.<br /><br />Using <a href="http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx">Process Explorer</a> (awesome tool, link is to Microsoft) on the process, I find that some thread with start address in <span style="font-weight: bold; font-style: italic;font-family:courier new;" >MSVCR80.dll</span> is eating all the CPU, while the process also seems to constantly spawn dozens of new threads per second with this dll as startpoint (they die at the same rate, so there is no buildup). <a href="http://www.google.no/search?q=MSVCR80.dll+Firefox">Googling</a> <a href="http://support.mozilla.com/tiki-view_forum_thread.php?locale=fi&forumId=1&comments_parentId=97238">around</a> I find a tip: <span style="font-weight: bold;">Disabling the plugin AVG Safe Search immediately zeroes the CPU load</span> (and also the thread spawning - there is no threads showing MSVCR80.dll anymore. The dll is apparently nothing more than MS C Runtime Library - I have no idea why it ends up like this, maybe it is just the thread entry point?).<br /><br />AVG installs two plugins, one "AVG Safe Search", the one that kills your machine, and one "AVG Security Toolbar", which gives NOTHING besides providing a Yahoo search box, obviously for hope of revenue from sending searches Yahoo's way, and <span style="font-style: italic;">eating lots of screen estate</span>. Amazingly, <span style="font-style: italic;">I find that AVG also changes the </span><span style="font-style: italic;">Search box of Firefox to Yahoo, <span>and that <span>as long as AVG Toolbar is enabled, it is impossible to change the Search box away from Yahoo</span></span>, not even over to e.g. Wikipedia!!</span> And I now also found that <span style="font-style: italic;">AVG has also hijacked the default browser search engine</span> (when you type something into the location bar) to Yahoo - an option that you apparently need to go to about:config to change back. Also, <span style="font-style: italic;">it is not possible to remove the AVG Firefox plugins</span> by means of Firefox's own plugin manager - but you can disable it.<br /><br />In this regard, AVG is nothing more than a seriously nasty "browser toolbar".<br /><br />Besides this buggy code and annoying toolbar, AVG really takes a boatload of time to run through less than half a TB of disk (even though none of the files changed), daily, and pretty much pegs the CPU at full throttle, and also "thrashes" all the memory of all other running processes onto disk, since it races through files and probably hence churns the OS's cache. The end result is that my own machine is completely unusable when "AVG is running scans" - so if I sit at it when it starts, I have to kill it, and if I come back to it when it still is running, I have to kill it. To top it, sometimes the tray icon hangs, so it is not possible to kill the scanning by normal means either (although you can just kill it from some process explorer still).<br /><br />And finally, there is the concept of AVG Free. That is all nice and well and good, but the point here is that it is only free for about a year. After that, you have to update. You can update to a new installation of AVG Free, but they hide this fact very good. The point is obviously that they hope and assume that people like me, "tech savvy nerds", install this onto computers of their friends and relatives - it is free and all!! However, after about a year, these poor friends and relatives will CONSTANTLY be bugged about their machine being at risk blah blah, and redirected to an AVG update page. This page will pretty much force them to pay (unless they themselves are savvy enough to understand that beneath one of the extremely small graphics on that page they might find a new installation of AVG Free). So, yes, my father is currently an AVG customer - which in itself isn't bad, but he has been lured into becoming one - and I was the unwitting sales agent for AVG.<br /><br />This is a technique I find bordering on adware.<br /><br />I will never recommend AVG again.Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com42tag:blogger.com,1999:blog-7596516389741282176.post-60435962149822675872009-03-23T14:36:00.006+01:002009-03-23T15:02:21.363+01:00Installing Debian on "Slug" NSLU2<i>Essence of this blogpost: Log in with a shell on the device to tail the syslog while the installer runs for much finer grained feedback during install. This can be OK, since it often takes "half a year" to format the disk and install the device, and one start to wonder whether it is hung.</i><br /><br />How cool: You can install the actual Debian newest version 5.0 system onto a Slug!<br /><br />This is a quick note on the installation of the Cisco Linksys NSLU2 device.<br /><br />The debian install works like this: You "upgrade" the device "to the installer" using one of three methods, one of which is to simply use the web console of the device and direct that to the "di-nslu2.bin" file you downloaded.<br /><br />The device then reboots, and after some minutes emits three beeps. You now log in to it using ssh (for Windowers, this means <a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html">putty</a>). The standard ip of any new Slug is 192.168.1.77, but if you enabled DHCP using the web console before you "updated the firmware", it will still use DHCP after booting, normally getting the address it got the first time (DHCP clients always asks for the same IP they got the first time from a DHCP server), or being given the static configured DHCP IP address you've configured on the DHCP server (which IMHO is the right thing to do).<br /><br />The username/password is installer/install. You end up on the "Network console for the Debian installer". Here you can select either "install it damnit" or "install (expert mode)", or "start shell".<br /><br />You select one of the install options, and you're on your way.<br /><br />The point of this blogpost is that the installer takes some time (like in the 4 hour range!). And just the formatting of a 1TB disk takes at least a full hour, where at least 95% of the time the progress bar shows "33% finished"! This makes one wonder what is happening.<br /><br />What is cool, is that one can log in once more to the installer. This time, select "Start shell". Now tail the syslog:<br /><code><br />cd /var/log<br />tail -f syslog<br /></code><br /><br />.. or, when the device is formatting your disk, which takes ages, instead tail "partman". Ctrl-C to stop. Since the Slug has very little memory, you should not do much creative stuff with the shell you've got there during the install, or else the installer itself might be terminated by Linux' OutOfMemoryKiller.Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com2tag:blogger.com,1999:blog-7596516389741282176.post-75648521237945667552009-03-17T19:11:00.003+01:002009-03-18T13:40:01.608+01:00Jonathan Schwartz censors blog comments!!Wasn't that a "WOW! Are you kidding??"-title? I threw in a comment on <a href="http://blogs.sun.com/jonathan/">Jonathan Schwartz</a>'s <a href="http://blogs.sun.com/jonathan/entry/commercial_innovation_3_of_4">blogpost</a>, but I guess it was way to radical to slip through the censorship.<br /><br />Update 2009-03-18, regarding the comment "Hope it pans out": <a href="http://www.theregister.co.uk/2009/03/18/ibm_buying_sun/">IBM might buy Sun</a>, so maybe this isn't exactly panning out at all! I find this sad - I've always appreciated this <a href="http://en.wikipedia.org/wiki/Stanford_University_Network">Standford University Network</a> "<a href="http://en.wikipedia.org/wiki/Sun_Microsystems#History">spinoff</a>" and the stuff they have produced. It will be a pretty hard smack in the face for the Open Source business concept if Sun now completely fails with Schwartz' large idea.<br /><br /><i>" Interesting blogposts; frank and open. I really enjoy them. The logic even seems sound, I hope it pans out!<br /><br />However, seeing that you changed your ticker to JAVA and all, it clearly seems you think Java actually has a bit of value. I would thus love for you to comment on and explain why JavaFX is a better idea than actually making java on the desktop better and potentially also keep up the idea behind lwuit for those "other screens of my life" (or however that jingle goes) which actually seemed to have picked up a small momentum, or at least interest.<br /><br />I bet Microsoft and Apple are having a great time for each blunder you do with java on the desktop: You could by this time "owned" much of the development on the desktop and we could all thereby had proper crossplatform software, had you focused on making the java platform "agile" in a much larger way: Footprint reduction and heavy modularization, startup time closer to native and flash, automatic updates, cross platform, media, Swing improvements and new components, applet handling and the list goes on. These things should have been driven a long time ago; they have been glaring and obvious problems all the time. Some of these things you JUST started doing, WAY late but still, with java 6 u10 and the direction this was taking, and then, absurdly, you totally switch direction into JavaFX, diverting focus and resources (and coincidentally it seems several of your best employees in these areas quit), and not least actually fragmenting java GUI'ing into a completely new language with components not easily used in the "old java", to the exceptional annoyance of, as I've understood it, /very/ much of your following. It thus time and time again seems like you pretty much ignore feedback from the community, instead going into totally irrelevant directions - loosing momentum and, importantly, lots of time.<br /><br />The fundament and the idea behind Java is so good, and the problems I listed aren't that huge - they just need focus, as update 10 showed - so it is just sad to see how you again and again seem to miss the /relevant/ boats! I sadly believe JavaFX will be an extremely tough hill to climb - why on earth would one use anything else than flash? Even Sun themselves use flash for all their presentations - even on these very blogposts. If anything, you might have helped drive the opening of flash and that open screen project, but I guess that wasn't the real aim. And in the meantime, everything else is delayed. Other actors are now taking over the leadership of Java, potentially (and already) fragmenting the place. And as an end comment; What's that absurd situation with Apache Harmony supposed to mean?! - that in no way moves Java forward. "</i>Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com3tag:blogger.com,1999:blog-7596516389741282176.post-86221681842716245942009-03-14T18:07:00.002+01:002009-03-14T18:12:15.903+01:00JCP's EC transparencyThis is hilarious:<br /> (From <a href="http://jcp.org/aboutJava/communityprocess/summaries/2008/September08-public-minutes.html">EC September 2008 Meeting Minutes</a>)<br /><br /><i><b>Transparency in EC meetings</b><br /><br />After a brief discussion of the need to make EC meetings more accessible to JCP members and to the general public, Roberto Chinnici proposed the following motion:<br /><br />"Minutes of EC meetings (including presentation materials) shall by default be public rather than EC-confidential. The EC will decide by a simple majority vote of those present whether or not to go into private session during which no votes may take place, but for which EC-private rather than public minutes will be published."<br /><br />Vicki Shipkowitz seconded the motion, which was passed unanimously.<br /><br />The ECs also agreed that Meeting Minutes will be accessible to all (not just to JCP members). The PMO will remove the password-protection from all previously-published Meeting Summaries, and will remove any "JCP EC Confidential" notices from these summaries. Previously-published Meeting Minutes will continue to be accessible only to EC members. These changes will be effective immediately.<br /><br /><b>Private Session</b><br /><br />The ECs then went into private session for a discussion on the negotiations between Apache and Sun.</i><br /><br />HAHAHA!Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com2tag:blogger.com,1999:blog-7596516389741282176.post-61812856325320321062009-03-10T15:59:00.010+01:002009-03-11T06:13:02.578+01:00Friends for Java v2Here's a new attempt at implementing a secure version of something akin to the <a href="http://en.wikipedia.org/wiki/Friend_class">friend class</a> concept in java, since the <a href="http://tech.stolsvik.com/2009/03/friends-for-java.html">previous attempt</a> was rather flawed in view of being used in a "neat'n'clean API" scenario.<br /><br />This time I'm using a little introspection to try to catch evil code. Hopefully this check can't be circumvented, at least if a SecurityManager is in place, and also hopefully won't be stopped by any SecurityManager (it doesn't immediately seems so: one can use getClass() at any time, right? Can one also use the 1.5 getEnclosingClass() without being stopped?)<br /><br /><span style="font-size:78%;">(If you use Eclipse, you can mark the entire code below (all classes in one go), Ctrl+C, then activate the project node of a project and hit Ctrl+V. You will probably have to organize imports afterwards).</span><br /><pre>package com.example;<br /><br />import api.WantedCode;<br /><br />public class NeedingCode {<br /> // Driver<br /> public static void main(String[] args) {<br /> // .. we have an instance of this class<br /> WantedCode code = new WantedCode();<br /><br /> // And access its package private method through a "friend-proxy"<br /> FriendProxy.getProxy().invokeWantedMethodOf(code);<br /> }<br />}<br /><br />// ------------<br /><br />package com.example;<br /><br />import api.WantedCode;<br /><br />public abstract class FriendProxy {<br /><br /> // :: Proxy interface method<br /><br /> public abstract void invokeWantedMethodOf(WantedCode instance);<br /><br /> // :: Friend infrastructure<br /><br /> private static FriendProxy _proxy;<br /><br /> public static void setProxy(FriendProxy proxy) {<br /> // Verify that the origin of this proxy matches our expectations.<br /> if (proxy.getClass().getEnclosingClass() != WantedCode.class) {<br /> throw new IllegalAccessError("Only my friend can invoke this method.");<br /> }<br /> _proxy = proxy;<br /> }<br /><br /> // :: Package-private accessor to get to friend's package private method.<br /><br /> static FriendProxy getProxy() {<br /> return _proxy;<br /> }<br />}<br /><br />// ------------<br /><br />package api;<br /><br />import com.example.FriendProxy;<br /><br />public class WantedCode {<br /><br /> void wantedMethod() {<br /> System.out.println("Hi from the package-private method in the API!");<br /> }<br /><br /> static {<br /> FriendProxy.setProxy(new GivingAccessToNeedingCode());<br /> }<br /><br /> private static class GivingAccessToNeedingCode extends FriendProxy {<br /> @Override<br /> public void invokeWantedMethodOf(WantedCode instance) {<br /> instance.wantedMethod();<br /> }<br /> }<br />}<br /><br />// ------------<br /><br />package com.example;<br /><br />import api.WantedCode;<br /><br />/**<br /> * Test whether the evilness is caught.<br /> */<br />public class EvilCode {<br /> // Driver<br /> public static void main(String[] args) {<br /> WantedCode code = new WantedCode();<br /> FriendProxy.setProxy(new FriendProxy() {<br /> @Override<br /> public void invokeWantedMethodOf(WantedCode instance) {<br /> System.err.println("I'm evil!");<br /> }<br /> });<br /> FriendProxy.getProxy().invokeWantedMethodOf(code);<br /> }<br />}<br /></pre>Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com10tag:blogger.com,1999:blog-7596516389741282176.post-17233693510888127392009-03-09T21:55:00.017+01:002009-03-11T06:13:27.814+01:00Friends for javaI read a <a href="http://kennke.org/blog/2009/01/08/friends-for-java/">blog entry</a> of <a href="http://kennke.org/~roman/resume.html">Roman Kennke</a>. He's talking about implementing something akin to the <a href="http://en.wikipedia.org/wiki/Friend_class">friend class</a> concept in java. The problem goes like this:<br /><pre>package api;<br /><br />public class WantedCode {<br /> void wantedMethod() {<br /> System.out.println("Wanted method in com.example");<br /> }<br />}<br /><br />// --- in another package ---<br /><br />package com.example<br /><br />import api.WantedCode;<br /><br />public class NeedingCode {<br /> void needingMethod() {<br /> // .. we have an instance of this class<br /> WantedCode instance = new WantedCode();<br /> // And need to access some package private method in that class<br /> instance.wantedMethod() // <- error, since it is package private<br /> }<br />}<br /></pre>The solution is to make an interface within NeedingCode's package, which declares a method that will invoke the package private method in WantedCode, taking as argument the instance of WantedCode on which the method shall be invoked. This interface will be implemented somewhere in WantedCode's package, and then statically set somewhere in NeedingCode's package. The corresponding getter is package private, and one has thereby established a "friend link" for this method which only can be used by NeedingCode's package.<br /><br />I had some problems following the post since the code snippets was so fragmented, so here I've dumped the full code, and also hopefully answered my own question put forward in the post: Anyone could set the proxy instance, thereby redirecting NeedingCode's invocations to some evil code. The idea is to use a two-way "handshake" to make sure that only the friend sets the proxy, by use of a "secret" (a private Object instance).<br /><br />Update: Roman Kennke pointed out the obvious: This pretty much ruins the original intent, as one then have ended up with a public facing "magic method" that is shown in the API (and one could then basically just have let the package-private method be public instead). On the other hand, this method can only be used for the sole purpose of establishing the specific friend aspect, and only to the selected other package - none other can make any use of it. Furthermore, if one can accept <span style="font-style:italic;">one</span> such public facing static magic method in the API-package, one could use this method to bridge this package (and any package-private method in that package) to any other (internal) packages.<br /><br />I've made a <a href="http://tech.stolsvik.com/2009/03/friends-of-java-v2.html">second attempt</a>! That one uses introspection to verify the origin of the supplied proxy instance.<br /><br /><span style="font-size:78%;">(If you use Eclipse, you can mark the entire code below (all classes in one go), Ctrl+C, then activate the project node of a project and hit Ctrl+V. You will probably have to organize imports afterwards).</span><br /><pre>package com.example;<br /><br />import api.WantedCode;<br /><br />public class NeedingCode {<br /> // Driver<br /> public static void main(String[] args) {<br /> // .. we have an instance of this class<br /> WantedCode code = new WantedCode();<br /><br /> // And access its package private method through a "friend-proxy"<br /> FriendProxy.getProxy().invokeWantedMethodOf(code);<br /> }<br />}<br /><br />// ------------<br /><br />package com.example;<br /><br />import api.WantedCode;<br /><br />public abstract class FriendProxy {<br /><br /> // :: The proxy interface aspect<br /><br /> public abstract void invokeWantedMethodOf(WantedCode instance);<br /><br /> // :: "Handshake" that establishes friendship<br /><br /> private final static Object _secret = new Object();<br /><br /> static {<br /> WantedCode.makeFriends(_secret);<br /> }<br /><br /> private static FriendProxy _proxy;<br /><br /> public static void setProxy(FriendProxy proxy, Object secret) {<br /> if (secret != _secret) {<br /> throw new IllegalAccessError("Cannot set proxy without correct secret.");<br /> }<br /> _proxy = proxy;<br /> }<br /><br /> // :: Package-private accessor to get to friend's package private method.<br /><br /> static FriendProxy getProxy() {<br /> return _proxy;<br /> }<br />}<br /><br />// ------------<br /><br />package api;<br /><br />import com.example.FriendProxy;<br /><br />public class WantedCode {<br /><br /> void wantedMethod() {<br /> System.out.println("Hi from the package-private method in the API!");<br /> }<br /><br /> // :: 2nd part of "handshake" that establishes friendship<br /><br /> public static void makeFriends(Object secret) {<br /> FriendProxy.setProxy(new GivingAccessToNeedingCode(), secret);<br /> }<br /><br /> private static class GivingAccessToNeedingCode extends FriendProxy {<br /> @Override<br /> public void invokeWantedMethodOf(WantedCode instance) {<br /> instance.wantedMethod();<br /> }<br /> }<br />}</pre>Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com6tag:blogger.com,1999:blog-7596516389741282176.post-15015049742833353372009-03-03T20:25:00.081+01:002009-10-02T16:46:01.414+02:00AWT Swing Event Pumping and TargetingI'm currently learning Swing, and thus AWT. In the post <a href="http://tech.stolsvik.com/2008/09/debug-listeners-on-awtswing-components.html">"Debug Listeners on AWT/Swing components"</a>, I wrote a little piece of code that can help with understanding what events are fired on a component.<br /><br />This is a further dive into the lower level elements of the event system of AWT/Swing. I wrote this as research notes for myself, but have then tried to edit it into something more readable, hoping that a wider audience than me alone can get any value from it.<br /><br /><span style="font-weight: bold;">Intended audience</span>: Somewhat experienced developers of the type that likes to know how things really work, and who subscribe to <a href="http://www.joelonsoftware.com/articles/LeakyAbstractions.html">Joel's law of Leaky Abstractions</a>. If you're a AWT/Swing guru that for example know how the current version of AWT handles the AWT 1.0 event model and where exactly in the event dispatch and processing chain the AWTEventListeners are invoked, you probably don't need this. If you've never written some hello world for Swing, it might be too early - but you could skim it nevertheless, to know if it will be interesting for you later.<br /><br />In the following text, which is a long article more than a blogpost, I ended up covering quite a bit. The main elements are:<ol><li>Basics of AWTEvents, low-level events and semantic (high-level) events.</li><li>Event pumping, Operating System to Java interaction and the transfer of events.</li><li>Event dispatching, Event Dispatch Thread ("EDT")</li><li>Event processing.</li><li>InputEvent (mouse and keyboard) retargeting, Focus subsystem</li><li>Some misc topics: Coalescing, AWTEventListeners, Key Bindings, and how semantic events are produced and dispatched (Spring's MVC logic).</li></ol><span style="font-weight:bold;">Please be advised</span> that there might be places where I haven't understood things correctly! The text is neither supposed to be a complete discussion of all the intricacies of Swing/AWT. However, the text might hopefully, for some level of developers, shed light on some elements of "how it really works". The text is supposed to progress in a somewhat orderly fashion - if it doesn't, just read it a couple of times more! If you have any corrections, ideas for making it easier to understand this stuff, or otherwise have any suggestions, please <a href="http://tech.stolsvik.com/2009/03/awt-swing-event-pumping-and-targeting.html#comments">comment</a> - or send an email to <a href="mailto:Endre@Stolsvik.com">Endre@Stolsvik.com</a>.<br /><br />The text contains a bunch of stacktraces. These are taken from runs on Java 1.6.0_12. However, these parts of the code don't change radically from version to version, and the overall discussion and even the stacktraces (less the exact line numbers) will probably still be relevant in some years from now.<br /><br /><span style="font-weight: bold;font-size:130%;" >Basics</span><br /><br /><span style="font-weight: bold;">Different types of events</span>: The event system comprises two distinct types of events: <span style="font-style: italic;">Low-level events</span>, and <span style="font-style: italic;">Semantic events</span>. The low-level events all extend <a href="http://java.sun.com/javase/6/docs/api/index.html?java/awt/event/ComponentEvent.html">ComponentEvent</a>, while the semantic events are all others, for example ActionEvent, AdjustmentEvent, ItemEvent, TextEvent and others. The low-level events are direct events concerning the GUI system and represent window-system occurrences or low-level input, like a window being moved or minimized, a key being pressed on the keyboard, the mouse being moved, a mousebutton being clicked. The semantic events, on the other side, pretty much all describe changes of state on Components, like a button being clicked, a menu item being choosen, text being entered into a field. Semantic events are typically constructed and dispatched by the components themselves based on low-level events.<br /><br />Here's a rather old (Feb 1997) article from Sun: <a href="http://java.sun.com/j2se/1.3/docs/guide/awt/designspec/events.html">"Java AWT: Delegation Event Model"</a>, concerning the change from Java 1.0 event model to the Java 1.1 delegation event model. This model is still the one being employed, and the article is a rather easy read - just decide if the historic parts are worth paying attention to or not. Here's a link to the Swing UI tutorial about these two kinds of events: <a href="http://java.sun.com/docs/books/tutorial/uiswing/events/generalrules.html#twokinds">"Concepts: Low-Level Events and Semantic Events"</a>. Here's a link to the event package JavaDoc: <a href="http://java.sun.com/javase/6/docs/api/index.html?java/awt/event/package-summary.html">Package java.awt.event</a>.<br /><br /><span style="font-style:italic;">This article will primarily concern the low-level events.</span><br /><br /><span style="font-weight: bold;">All AWT/Swing events are of supertype AWTEvent</span>: The events fired on the different listeners are extensions of <a href="http://java.sun.com/javase/6/docs/api/index.html?java/awt/AWTEvent.html">AWTEvent</a>, for example <a href="http://java.sun.com/javase/6/docs/api/index.html?java/awt/event/MouseEvent.html">MouseEvent</a>, and the specific subtype of mouse event is communicated by which of the different methods on the <a href="http://java.sun.com/javase/6/docs/api/index.html?java/awt/event/MouseListener.html">MouseListener</a> (or <a href="http://java.sun.com/javase/6/docs/api/index.html?java/awt/event/MouseMotionListener.html">MouseMotionListener</a> or <a href="http://java.sun.com/javase/6/docs/api/index.html?java/awt/event/MouseWheelListener.html">MouseWheelListener</a>) is invoked, for example MouseListener.mouseClicked(MouseEvent e). AWTEvent also has a method getID() that describes which type (e.g. "mouse") and subtype (e.g. "mouse moved") the event is (MouseEvent ids starts at 500, and MOUSE_MOVED is 503). AWTEvent is a subclass of Event, which has a getSource() method. The source always refers to the component "in question": Low-level events originate from the windowing system, and the source is the component that got or will get the Event, and it can thus quite cleanly be thought of as the <span style="font-style: italic;">target</span>. High-level events are typically created by the components themselves, and are thus originating from that component, making "source" correcter. When processing events, the EventDispatchThread, which is discussed later, directly invokes getSource() on the event, and then invokes source.dispatchEvent(event). The specific event classes add event type specific stuff, for example MouseEvent which amongst others has methods getLocationOnScreen() and getButton().<br /><br />Here's the Sun tutorials index for EventListeners: <a href="http://java.sun.com/docs/books/tutorial/uiswing/events/index.html">"Lesson: Writing Event Listeners"</a>, which has lots of good information that hopefully will make even more sense after reading this article. In particular the listing in <a href="http://java.sun.com/docs/books/tutorial/uiswing/events/eventsandcomponents.html">"Listeners Supported by Swing Components"</a> is good for an overview or index over what event listeners are supported by which components. Note how it divides the list into low-level events ("Listeners that All Swing Components Support" - the AWT events) and higher-level semantic event listeners ("Other Listeners that Swing Components Support"). From <a href="http://java.sun.com/developer/Books/GJ21AWT/">this page</a> you can get <a href="http://java.sun.com/developer/Books/GJ21AWT/ch9.pdf">a pdf of Chapter 9</a> of the book "Mastering the JFC: AWT, Volume 1", which is a 2001 book but which still is relevant.<br /><br /><span style="font-weight: bold;font-size:130%;" >From the Operating System to the MouseListener</span><br /><br /><span style="font-weight: bold;">Native Event Loop</span>: The Toolkit implementation and its corresponding event pumping thread is the native connection to the underlying operating system, and is thus specific for each operating system. Inside the Toolkit thread event loop, extension classes of AWTEvent (e.g. MouseEvent) are instantiated and posted to the EventQueue. The source is set to the appropriate AWT Window: For example, for KeyEvents, this is the active window, while for MouseEvents, it is the window below the mouse. The AWTEvent is "posted" to the java side by some roundabout ways which effectively ends up in an invocation of EventQueue.postEvent(AWTEvent). The specific Toolkit instance along with the toolkit thread forms the native side of the GUI event pumping.<br /><br />The source code for the native Toolkit implementations aren't available in the standard SDK distribution. Here's a <a href="http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Platform/windows/sun/awt/windows/WToolkit.java.htm">link to WToolkit</a>, the Windows Toolkit implementation. Notice the bunch of native methods. Notice the native method eventLoop() at line 303 and the call to this from the Runnable of the "AWT-Windows" thread at line 289.<br /><br />Here's a stacktrace of a posting of a MouseEvent.MOUSE_MOVED event from the native Toolkit thread, named "AWT-Windows". Notice how it doesn't post it directly onto the EventQueue, but rather onto a "intermediate storage point" called PostEventQueue:<br /><br /><span style=";font-family:courier new;font-size:78%;"> Daemon Thread [AWT-Windows]<br /> sun.awt.PostEventQueue.postEvent(java.awt.AWTEvent) line: 2083<br /> sun.awt.SunToolkit.postEvent(sun.awt.AppContext, java.awt.AWTEvent) line: 591<br /> sun.awt.windows.WFramePeer(sun.awt.windows.WComponentPeer).postEvent(java.awt.AWTEvent) line: 722<br /> sun.awt.windows.WToolkit.eventLoop() line: not available [native method] [local variables unavailable]<br /> sun.awt.windows.WToolkit.run() line: 291 java.lang.Thread.run() line: 619<br /></span><br /><span style="font-weight: bold;">Java Event Loop</span>: The <span style="font-style: italic;">EventDispatchThread</span> ("EDT") is a thread that basically repeatedly invokes EventDispatchThread.pumpOneEventForFilters(int id) in a loop. This again invokes EventQueue.dispatchEvent(AWTEvent e). The <a href="http://java.sun.com/javase/6/docs/api/index.html?java/awt/EventQueue.html">EventQueue</a> and EventDispatchThread together forms the java side of the event pumping. Upon dispatch, the EventQueue does checks what type of Event is being handled. ActiveEvents has a method dispatch() which is invoked directly. If the source of the AWTEvent is a Component, this component has its dispatchEvent(AWTEvent) invoked with the event as the argument.<br /><br />The EventQueue gets events posted either from the native Toolkit thread or from the application code ("userland"). EventQueue.invokeLater(Runnable) and invokeAndWait(Runnable) posts an event to the EventQueue. Such events will be of type InvocationEvent (which is an ActiveEvent). It is also possible to directly post events onto the queue, as such: Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(event), but getting the EventQueue might be denied by the call to System.getSecurityManager().checkAwtEventQueueAccess(). Note that a lot of event firing, particularly for the higher-level events (the Semantic events), doesn't go through the EventQueue. For example, if a JButton is clicked, the generated ActionEvent is never posted to the EventQueue, but instead fired on the button's ActionListeners directly ("directly" is relative; The event is fired through or rather by Swing's Model-View-Controller paradigm, but still not through the EventQueue - more on this later).<br /><br />Continuing on the MouseEvent.MOUSE_MOVED example started above: The EventQueue is notify'ed, leading to a flush of events from the PostEventQueue into the EventQueue. The thread here is the java side, the EventDispatchThread, named "AWT-EventQueue-0" (The zero at the end of the name is increased if this thread crashes, in which case a new one is created when the next event is posted by the Toolkit, and also increased for modal dialogs):<br /><br /><span style=";font-family:courier new;font-size:78%;"> Thread [AWT-EventQueue-0]<br /> java.awt.EventQueue.postEvent(java.awt.AWTEvent, int) line: 244<br /> java.awt.EventQueue.postEventPrivate(java.awt.AWTEvent) line: 202<br /> java.awt.EventQueue.postEvent(java.awt.AWTEvent) line: 175<br /> sun.awt.PostEventQueue.flush() line: 2072<br /> sun.awt.SunToolkit.flushPendingEvents() line: 626<br /> java.awt.EventQueue.getNextEvent() line: 465<br /> java.awt.EventDispatchThread.pumpOneEventForFilters(int) line: 236<br /> java.awt.EventDispatchThread.pumpEventsForFilter(int, java.awt.Conditional, java.awt.EventFilter) line: 184<br /> java.awt.EventDispatchThread.pumpEventsForHierarchy(int, java.awt.Conditional, java.awt.Component) line: 174<br /> java.awt.EventDispatchThread.pumpEvents(int, java.awt.Conditional) line: 169<br /> java.awt.EventDispatchThread.pumpEvents(java.awt.Conditional) line: 161<br /> java.awt.EventDispatchThread.run() line: 122<br /></span><br /><span style="font-style:italic;">Two-thread event pump</span>: Notice how there are thus two completely separate threads doing event pumping: One running on the native side, the Toolkit thread, pumping events from the native operating system "over to java" via posting to an intermediate PostEventQueue class, and then notifying the java side via the EventQueue. On the java side, we have the EventDispatchThread that waits on the EventQueue. When it gets a notify, it first flushes any new events from the native side into the EventQueue proper, and then pumps from the EventQueue.<br /><br /><span style="font-weight: bold;">Dispatching on the AWT and Swing Components</span>: The main dispatching method on to the AWT/Swing component side is Component.dispatchEvent(AWTEvent e), which is final, but invokes Component.dispatchEventImpl(AWTEvent) which is package-private and is overridden by Container and furthermore by Window. Handled events in these overrides include ComponentEvent.COMPONENT_RESIZED for Windows, in which case it invalidates and validates <span style="font-style: italic;">before</span> passing on to super, and resize and move events for Container, in which case it produces HierarchyEvent.ANCESTOR_[RESIZED|MOVED] for its children, <span style="font-style: italic;">after</span> it has invoked super. However, Container also takes care of all MouseEvents, doing retargeting and redispatch, which we will come back to.<br /><br />The Component.dispatchEventImpl(AWTEvent) is a rather long method that handles different aspects of different events, divided into steps. It takes care of a lot of special cases, both because of special requirements for some types of events, for the KeyboardFocusManager and lightweight component retargeting for events handled by focus, and for flexibility, but also because of lots of cruft in the event handling accumulated throughout the years <span style="font-size:78%;">(In particular this goes for the Java 1.0 AWT style of event handling (explained in the article <a href="http://java.sun.com/j2se/1.3/docs/guide/awt/designspec/events.html">"Java AWT: Delegation Event Model"</a> referenced above) whereby one had to subclass a component to get events, which then were delivered by the Component's "self-invocation" of for example the overridden Component.mouseDown(Event e))</span>. The Component.dispatchEventImpl(e) are nicely commented inline, clearly showing each stage of the dispatch. Unless some special case kicks in (of which there are many, in particular input event retargeting, described shortly), the method processEvent(e) is eventually invoked in step "6. Deliver event for normal processing", but only if Component.eventEnabled(AWTEvent e) returns true. This method returns true if either the event-type specific listener field is non-null, or if the eventMask, set by enableEvents(long), says so. The processing is described shortly.<br /><br /><span style="font-weight: bold;">InputEvents (re)targeting</span>: The (currently) two types of <a href="http://java.sun.com/javase/6/docs/api/index.html?java/awt/event/InputEvent.html">InputEvents</a> have distinct targeting logic, meaning which Component's Listeners shall be invoked. MouseEvents goes to the most specific ("deepest") child component residing under the mouse, while KeyEvents are sent to the focused window, routed using the KeyboardFocusManager to the focused component in this window.<br /><br /><span style="font-style:italic;">MouseEvents targeting</span>: Container overrides dispatchEventImpl(AWTEvent). Remember that Window is a Container. If the Container in question is a heavyweight component (i.e. it is not an instanceof LightweightPeer), this override takes care of finding the target for MouseEvents, attempting via an instance of the class LightweightDispatcher to forward/distribute MouseEvents to the most specific ("deepest") child, whether this is a heavyweight AWT Component, or a lightweight JComponent. These days this class has an incorrect name, since it also is responsible for retargeting MouseEvents to any heavyweight Components. (The class is package private, residing in Container.java).<br /><br />To find the deepest component, Container.getMouseEventTarget(x, y ...) is invoked, which goes through each child component, finding the one that returns true for Component.contains(x, y). If the child is a Container, it recurses by invoking getMouseEventTarget(x - comp.x, y - comp.y ...) on the child (hence upholding the "illusion" that 0,0 is the local top left corner for all component). Since JComponents extends from Container, this method also checks itself after the recurse. The check mentioned is whether it wants any MouseEvents by testing whether the eventMask says so (mentioned above), or if any of the three types of mouse listeners are set (Mouse, MouseMotion, MouseWheel). The net effect is that the LightweightDispatcher thus finds the deepest Component which the mouse is currently over and which wants MouseEvents, retargets the event by making a new one, dispatches the new event instance directly, and consumes the original event instance. Or it finds that there are no such component, either because there physically aren't any components there (the window background is showing), or because the component don't want MouseEvents (for example a standard JLabel doesn't), in which case the LightweightDispatcher won't do anything (it won't consume the event), and the event will be dispatched as normal (on itself, the native Container, typically a Window). <br /><br />LightweightDispatcher also tracks mouse enter and exits over lightweight components as the native system doesn't know about them and thus cannot make enter/exit events for them either. Also, if we're dragging, it hooks an AWTEventListener on the Toolkit (as mentioned below), so that it gets all further MouseEvents even though we drag across different boundaries of lightweight and heavyweight components.<br /><br />If the original event is consumed, the LightweigthDispatcher dispatchEvent returns true, and the Container.dispatchEvent returns. If it doesn't consume the event (hence, it was no MouseEvent, or it was a MouseEvent targetted at the Container itself), it returns false, and the Container.dispatchEvents continues by invoking super.dispatchEventImpl(AWTEvent e), and hence normal dispatch ensues.<br /><br /><span style="font-style:italic;">KeyEvent targeting / Focus Subsystem</span>: As briefly mentioned, Component.dispatchEventImpl(e) also handles keyboard targeting: KeyboardFocusManager's static method retargetFocusEvent() is invoked, apparently to handle actual FOCUS_GAINED and FOCUS_LOST events. Then, the KeyboardFocusManager is statically asked for the current KeyboardFocusManager (I am not sure if it really is possible to entirely roll your own KFM, as the abstract class KFM specifically states several places that it depends on the code in DefaultKeyboardFocusManager), and dispatchEvent(e) is invoked on that. If this invocation returns true, the event was dispatched, and we are finished. It is possible to register KeyEventDispatcher instances on the KeyboardFocusManager, which can consume the event - the focused component is already established at that point, being set in the KeyEvent (getSource(), or rather getComponent() since it is a ComponentEvent - read towards the top, "All AWT/Swing events are of supertype AWTEvent"). Here's the Sun Java tutorial: <a href="http://java.sun.com/docs/books/tutorial/uiswing/misc/focus.html">"How to use the Focus Subsystem"</a>. For a in-depth description of the focus subsystem, read the article <a href="http://java.sun.com/javase/6/docs/api/java/awt/doc-files/FocusSpec.html">The AWT Focus Subsystem</a>, referenced from the Component class and in particular its processKeyEvent method. This subsystem is somewhat complex, including logic about multiple focus cycle roots and how the different components are notified about focus gained and lost events. An interesting note is that if you end up in a more "inner" focus cycle root, there are by default no keypresses that can lift you out to the outer root again.<br /><br />Note that for JComponents, there is another way to hook Actions to specific KeyStrokes, namely the InputMap/ActionMap system, which is described below.<br /><br /><span style="font-weight: bold;">Event processing on Component</span>: As mentioned above, Component.processEvent(AWTEvent e) is eventually invoked. It has a bunch of "downstream" methods: process[Component|Focus|Key|Mouse[Motion|Wheel]|Input|Hierarchy[Bounds]]Event. All these methods can be overridden in subclasses, thus providing interesting (and non-trivial) hook-points. If you do override them, remember enableEvents(bitfield), described shortly. It is these downstream methods, for example processMouseEvent, that eventually invokes the listeners, for example MouseListener. <br /><br />For any type of event, there can be several EventListeners on a Component. In the Component class, there is a sole field for each type, e.g. keyListener. Nullness of this field is used as indicator: If it is null, there is no listeners for that event type. If it is non-null, there is at least one. If there is one listener, it will be the actual listner. If there are several listener, the field will be an AWTEventMulticaster instance, which is a somewhat clever way of simply daisy-chaining a set of listeners of the same type into a string of listeners whose event fire methods all will be invoked when the AWTEventMulticaster's corresponding fire-method is invoked. AWTEventMulticaster obviously implements all types of AWT EventListeners. The order in which listeners of a specific type is invoked is indeterminate, thus if important, you must handle this in some way yourself.<br /><br />Events are not delivered to a Component unless there are at least one listener for this event, <span style="font-style: italic;">or</span> if enableEvents(long eventsToEnable) is invoked (in which case processEvent(e) is invoked even though there are no listeners for that type). The long is a bit-mask, composed by ORing together the constants in AWTEvent. The process[Type]Event does the null-check on the corresponding typeListener field, returning immediately if null. If non-null, it invokes the correct method on the TypeListener based on a switch on event.getID(). All process[*]Event methods are protected and non-final, hence overridable for subclasses.<br /><br /><span style="font-weight:bold;">MouseEvent.mouseClicked</span>: Here is a stacktrace for invocation of mouseClicked(e) on a <a href="http://java.sun.com/javase/6/docs/api/index.html?java/awt/event/KeyListener.html">MouseListener</a> installed on a JLabel when I clicked the mouse. The stacktrace begins in the EventQueue where an event is pumped. It is dispatched on the Window instance where it goes to the Window and Container specific overrides of dispatchEventImpl. It is a MouseEvent, so it goes through the LightweightDispatcher where this MouseEvent is retargeted (a new MouseEvent is created, the old is consumed). The LightweigthDispatcher directly redispatches the new MouseEvent on the JLabel instance where it is processed. Finally, it ends up in the ExampleMouseListener as an invocation on its mouseClicked(e) method. <span style="font-size:78%;">(In this example, the JComponent also intervenes to handle "Autoscroll". This is a feature that enables moving of a scrollpane by dragging the content: If you setAutoscrolls(true), synthetic MouseDragged events will be made if you click inside the component and then drag the mouse, even though you drag it outside the component. The override of the processMouseEvent is to stop this feature when you release the mouse button.)</span>. Here's Sun's tutorial on MouseListeners: <a href="http://java.sun.com/docs/books/tutorial/uiswing/events/mouselistener.html">"How to write a Mouse Listener"</a>. The thread running is, as always for Java GUI stuff, the EventDispatchThread.<br /><br /><span style=";font-family:courier new;font-size:78%;"> Thread [AWT-EventQueue-0]<br /> com.example.ExampleMouseListener.mouseClicked(...)<br /> java.awt.Component.processMouseEvent(Component.java:6219)<br /> javax.swing.JComponent.processMouseEvent(JComponent.java:3265)<br /> java.awt.Component.processEvent(Component.java:5981)<br /> java.awt.Container.processEvent(Container.java:2041)<br /> java.awt.Component.dispatchEventImpl(Component.java:4583)<br /> java.awt.Container.dispatchEventImpl(Container.java:2099)<br /> java.awt.Component.dispatchEvent(Component.java:4413)<br /> ^^ This is a redispatch of a new MouseEvent, now on the JLabel, while the old event will be consumed.<br /> java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4556)<br /> ^^ It has now found the target Component: The JLabel<br /> java.awt.LightweightDispatcher.processMouseEvent(Container.java:4229)<br /> java.awt.LightweightDispatcher.dispatchEvent(Container.java:4150)<br /> ^^ The retargeting of the MouseEvent begins, by the LightweightDispatcher held by the Window instance.<br /> java.awt.Container.dispatchEventImpl(Container.java:2085)<br /> java.awt.Window.dispatchEventImpl(Window.java:2475)<br /> java.awt.Component.dispatchEvent(Component.java:4413)<br /> ^^ The initial dispatch, on the Window that holds the JLabel.<br /> java.awt.EventQueue.dispatchEvent(EventQueue.java:599)<br /> java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269) <font color="blue"><-- "EDT pump" references!</font><br /> java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)<br /> java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)<br /> java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)<br /> java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)<br /> java.awt.EventDispatchThread.run(EventDispatchThread.java:122)<br /></span><br /><span style="font-weight:bold;">KeyEvent.keyTyped</span>: To contrast, here's a stack trace for invocation of keyTyped(e) on a <a href="http://java.sun.com/javase/6/docs/api/index.html?java/awt/event/KeyListener.html">KeyListener</a> installed on a JButton. I made the button focused (by clicking on it, or by tabbing to it), then hit a key. The event is processed the same up to the Container.dispatchEventImpl, where the event <span style="font-style: italic;">is not</span> processed, and instead goes further to Component.dispatchEventImpl. Here, the KeyboardFocusManager kicks in since it is a KeyEvent, and does its magic focusing logic and where the KeyEvent is retargeted (a new KeyEvent is created, the old is consumed). The KeyboardFocusManager directly redispatches the new KeyEvent on the JButton, where it is processed. Finally, it ends up in the ExampleKeyListener as an invocation on its keyTyped(e) method. Here's Sun's tutorial on KeyListeners: <a href="http://java.sun.com/docs/books/tutorial/uiswing/events/keylistener.html">"How to write a Key Listener"</a>.<br /><br /><span style=";font-family:courier new;font-size:78%;"> Thread [AWT-EventQueue-0]<br /> com.example.ExampleKeyListener.keyTyped(...)<br /> java.awt.Component.processKeyEvent(Component.java:6171)<br /> javax.swing.JComponent.processKeyEvent(JComponent.java:2799)<br /> java.awt.Component.processEvent(Component.java:5993)<br /> java.awt.Container.processEvent(Container.java:2041)<br /> java.awt.Component.dispatchEventImpl(Component.java:4583)<br /> java.awt.Container.dispatchEventImpl(Container.java:2099)<br /> java.awt.Component.dispatchEvent(Component.java:4413)<br /> ^^ This is a redispatch of a new KeyEvent, now on the JButton, while the old will be consumed.<br /> java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1848)<br /> ^^ It has now found the target Component: The JButton<br /> java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(DefaultKeyboardFocusManager.java:704)<br /> java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(DefaultKeyboardFocusManager.java:969)<br /> java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:841)<br /> java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:668)<br /> ^^ The retargeting of the KeyEvent begins, by the global KeyboardFocusManager<br /> java.awt.Component.dispatchEventImpl(Component.java:4455)<br /> java.awt.Container.dispatchEventImpl(Container.java:2099)<br /> java.awt.Window.dispatchEventImpl(Window.java:2475)<br /> java.awt.Component.dispatchEvent(Component.java:4413)<br /> ^^ The initial dispatch, on the Window that holds the JButton.<br /> java.awt.EventQueue.dispatchEvent(EventQueue.java:599)<br /> <font color="blue">["EDT pump"]</font><br /></span><br /><br /><span style="font-weight: bold;font-size:130%;" >Misc topics</span><br /><br /><span style="font-weight: bold;">Coalescing</span>: This is a mechanism whereby a bunch of e.g. mouse-move events are compressed into one. This is done by checking whether the event currently-being-posted-to-the-Event-Queue could be collapsed into an already-posted-but-not-yet-processed event. Earlier, this was a general system whereby the source ("target") component would possibly go through the entire eventqueue looking for same-type events, checking whether it could collapse them, but this has been deemed too slow. The EventQueue.coalesceOtherEvent method that takes care of this legacy has the following comment: "<span style="font-style: italic;">Should avoid of calling this method by any means as it's working time is dependant on EQ length. In the wors case this method alone can slow down the entire application 10 times by stalling the Event processing. Only here by backward compatibility reasons.</span>" The present system uses the field Component.eventCache, where five specific event types can be handled: PaintEvent.PAINT, PaintEvent.UPDATE, MouseEvent.MOUSE_MOVED and MouseEvent.MOUSE_DRAGGED, in addition to sun.awt.PeerEvent (which I don't know what is) which apparently handles coalescing itself (it also handles dispatching itself - it is an ActiveEvent, specifically an extension of InvocationEvent). The coalescing is as dumb (and fast) as possible: mouse events are coalesced by keeping the latter event, while the paint events are coelesced by checking whether one or the other "update rect" is contained in the opposite, in which case it returns the containing, or else it doesn't coalesce.<br /><br /><span style="font-weight: bold;">AWTEventListener, "Event spying"</span>: On the Toolkit, one may install a "global listener" to spy on all low-level events that are dispatched from the EventQueue onto some Component: Toolkit.getDefaultToolkit().addAWTEventListener(listener, long eventMask), where the eventMask is the same bit-mask described Component.enableEvents(...) above. Adding an AWTEventListener might be denined by the call to System.getSecurityManager().checkPermission(SecurityConstants.ALL_AWT_EVENTS_PERMISSION).<br /><br />This is a stacktrace when such an AWTEventListener is invoked, also on the EventDispatchThread, illustrating both the EventQueue and the run through the dispatching on the Window, Container, Component. The invocation of AWTEventListeners are done early in the Component.dispatchEventImpl, at step "2. Allow the Toolkit to pass this to AWTEventListeners", which makes it possible for a AWTEventListener to consume the event early and thereby stop the dispatch process from going further.<br /><br /><span style=";font-family:courier new;font-size:78%;"> Thread [AWT-EventQueue-0]<br /> com.example.ExampleAWTEventListener.eventDispatched(...)<br /> java.awt.Toolkit$SelectiveAWTEventListener.eventDispatched(Toolkit.java:2353)<br /> java.awt.Toolkit$ToolkitEventMulticaster.eventDispatched(Toolkit.java:2244)<br /> java.awt.Toolkit.notifyAWTEventListeners(Toolkit.java:2203)<br /> ^^ The Toolkit is invoked to pass the event through the AWTEventListeners.<br /> java.awt.Component.dispatchEventImpl(Component.java:4481)<br /> java.awt.Container.dispatchEventImpl(Container.java:2099)<br /> java.awt.Window.dispatchEventImpl(Window.java:2475)<br /> java.awt.Component.dispatchEvent(Component.java:4413)<br /> java.awt.EventQueue.dispatchEvent(EventQueue.java:599)<br /> <font color="blue">["EDT pump"]</font><br /></span><br /><span style="font-weight: bold;">Key Bindings / Keyboard handling by InputMap and ActionMap</span>: Swing components, JComponents, have one more way of handling KeyEvents: <a href="http://java.sun.com/docs/books/tutorial/uiswing/misc/keybinding.html">The InputMap and ActionMap</a>. The InputMap defines a Map between KeyStrokes and "actionMapKeys" which (typically) are Strings that describes what this key does, while the ActionMap defines a Map between those actionMapKeys and some Action. Note that the actionMapKey arguement type is Object, but it is custom to use Strings so that these Maps become "self documenting".<br /><br />Read the JavaDoc of the static methods on KeyStroke carefully; They are a little devious! The whole key handling system reeks of legacy. The problem spots revolve around the fact that for KEY_TYPED events, the KeyEvent instance returns the typed character in the method getKeyChar(), while for KEY_PRESSED and KEY_RELEASED events, one needs to use the getKeyCode() (which returns a KeyEvent.VK_[key] constant). This also makes for interesting times when using the getKeyStroke methods - be sure to read the JavaDocs. In particular, <b>both</b> the methods getKeyStroke(int keyCode, int modifiers) and getKeyStroke(int keyCode, int modifiers, boolean onKeyRelease) return strokes for KEY_PRESSED and KEY_RELEASED (the latter if the boolean is true), meaning that there are no way to get a stroke for KEY_TYPED when using the key codes.<br /><br />This binding system is the functionality used by <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/menu.html#mnemonic">mnemonics</a> (picking a menu item from a selected menu by a key, typically this letter is underlined) and accelerators (picking some menu item without navigating the menu, e.g. "Ctrl-F" for File->Open). There is also a trick here for the UI (the Look and Feel): The UI have a separate parent Map that takes care of any Look'n'Feel specific key bindings, and makes it possible to change the look and feel without loosing the application specific bindings.<br /><br />Here's the JavaDoc from KeyboardManager, a package private helper class for the key bindings system, which I found nicely summing up the keybindings system:<br /><br /> "<span style="font-style: italic;"> The KeyboardManager class is used to help dispatch keyboard actions for the WHEN_IN_FOCUSED_WINDOW style actions. Actions with other conditions are handled directly in JComponent.</span><span style="font-style: italic;"><br /><br />Here's a description of the symantics of how keyboard dispatching should work atleast as I understand it.</span><span style="font-style: italic;"><br /><br />KeyEvents are dispatched to the focused component. The focus manager gets first crack at processing this event. If the focus manager doesn't want it, then the JComponent calls super.processKeyEvent() this allows listeners a chance to process the event.</span><span style="font-style: italic;"><br /><br />If none of the listeners "consumes" the event then the keybindings get a shot. This is where things start to get interesting. First, KeyStokes defined with the WHEN_FOCUSED condition get a chance. If none of these want the event, then the component walks though it's parents looked for actions of type WHEN_ANCESTOR_OF_FOCUSED_COMPONENT.</span><span style="font-style: italic;"><br /><br />If no one has taken it yet, then it winds up here. We then look for components registered for WHEN_IN_FOCUSED_WINDOW events and fire to them. Note that if none of those are found then we pass the event to the menubars and let them have a crack at it. They're handled differently.</span><span style="font-style: italic;"><br /><br />Lastly, we check if we're looking at an internal frame. If we are and no one wanted the event then we move up to the InternalFrame's creator and see if anyone wants the event (and so on and so on). </span>"<br /><br />Since this article has come to include a bunch of stack traces to illustrate things, I'll chuck in a couple more to show where the keybindings kick in the event processing. On a window with two buttons, I've bound F2 to JButton A "WHEN_FOCUSED", F3 to the containing JPanel "WHEN_ANCESTOR_OF_FOCUSED_COMPONENT", and finally F4 to the same JButton A "WHEN_IN_FOCUSED_WINDOW". Hitting key F2, F3 and F4 when the button A is focused "hits" on all keys, while if JButton B is focused, F2 doesn't fire, but F3 and F4 still does; F2 doesn't fire since it is not focused, F3 fires since it is bound to the ancestor JPanel of JButton B, while F4 fires since the window that JButton B resides in is focused.<br /><br />Here's the stacktrace for F2, which is bound to the JButton A "WHEN_FOCUSED", and fires when JButton A has focus:<br /><br /><span style=";font-family:courier new;font-size:78%;"> Thread [AWT-EventQueue-0]<br /> com.example.ExampleAction.actionPerformed(...)<br /> javax.swing.SwingUtilities.notifyAction(SwingUtilities.java:1636)<br /> javax.swing.JComponent.processKeyBinding(JComponent.java:2849)<br /> javax.swing.JComponent.processKeyBindings(JComponent.java:2884)<br /> ^^ Checking own bindings<br /> javax.swing.JComponent.processKeyEvent(JComponent.java:2812) <font color="green"><-- .. next trace starts here!</font><br /> java.awt.Component.processEvent(Component.java:5993)<br /> java.awt.Container.processEvent(Container.java:2041)<br /> java.awt.Component.dispatchEventImpl(Component.java:4583)<br /> java.awt.Container.dispatchEventImpl(Container.java:2099)<br /> java.awt.Component.dispatchEvent(Component.java:4413)<br /> ^^ This is a redispatch of a new KeyEvent, now on the JButton, while the old will be consumed.<br /> java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1848)<br /> ^^ It has now found the target Component: The JButton<br /> java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(DefaultKeyboardFocusManager.java:704)<br /> java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(DefaultKeyboardFocusManager.java:969)<br /> java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:841)<br /> java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:668)<br /> ^^ The retargeting of the KeyEvent begins, by the global KeyboardFocusManager<br /> java.awt.Component.dispatchEventImpl(Component.java:4455)<br /> java.awt.Container.dispatchEventImpl(Container.java:2099)<br /> java.awt.Window.dispatchEventImpl(Window.java:2475)<br /> java.awt.Component.dispatchEvent(Component.java:4413)<br /> ^^ The initial dispatch, on the Window that holds the JButton.<br /> java.awt.EventQueue.dispatchEvent(EventQueue.java:599)<br /> <font color="blue">["EDT pump"]</font><br /></span><br />Here's the stacktrace for F3, which is bound to the JPanel "WHEN_ANCESTOR_OF_FOCUSED_COMPONENT", and fires when either of the buttons (or anything else residing in that JPanel) has focus:<br /><br /><span style=";font-family:courier new;font-size:78%;"> Thread [AWT-EventQueue-0]<br /> com.example.ExampleAction.actionPerformed(...)<br /> javax.swing.SwingUtilities.notifyAction(SwingUtilities.java:1636)<br /> javax.swing.JComponent.processKeyBinding(JComponent.java:2849)<br /> ^^ This invocation is now on the JPanel and not the JButton anymore<br /> javax.swing.JComponent.processKeyBindings(JComponent.java:2895)<br /> ^^ Traversing parents<br /> javax.swing.JComponent.processKeyEvent(JComponent.java:2812) <font color="green"><--</font><br /> -- same as above<br /></span><br />And finally, here's the stacktrace for F4, which is bound to the JButton A "WHEN_IN_FOCUSED_WINDOW", and fires when either of the buttons (or anything else in the entire Window) has focus:<br /><br /><span style=";font-family:courier new;font-size:78%;"> Thread [AWT-EventQueue-0]<br /> com.example.ExampleAction.actionPerformed(...)<br /> javax.swing.SwingUtilities.notifyAction(SwingUtilities.java:1636)<br /> javax.swing.JComponent.processKeyBinding(JComponent.java:2849)<br /> javax.swing.KeyboardManager.fireBinding(KeyboardManager.java:267)<br /> javax.swing.KeyboardManager.fireKeyboardAction(KeyboardManager.java:216)<br /> ^^ Runs through all (showing) components that have registered WHEN_IN_FOCUSED_WINDOW maps.<br /> javax.swing.JComponent.processKeyBindingsForAllComponents(JComponent.java:2926)<br /> ^^ This is a static method, invoking the global KeyboardManager<br /> javax.swing.JComponent.processKeyBindings(JComponent.java:2918)<br /> ^^ Given up on local component bindings!<br /> javax.swing.JComponent.processKeyEvent(JComponent.java:2812) <font color="green"><--</font><br /> -- same as above<br /></span><br />It is worth noting that even if a key binding is bound to the InputMap WHEN_IN_FOCUSED_WINDOW for some component, the bound action will only fire if the component is <span style="font-style:italic;">showing</span> and <span style="font-style:italic;">enabled</span>. This means that if you have a JTabbedPane, adding a WHEN_IN_FOCUSED_WINDOW KeyBinding to some component on some tab, the action will only fire if this tab is the one showing - i.e. it won't fire if some other tab of that same JTabbedPane is showing. (The JTabbedPane alters the "visible" property of its contained components when changing tabs, which thus changes the showing property: Showing is when you are visible and all grandparents up to and including the window also are visible). This effect is probably what you want (!), but if not, then you'd obviously just do the key binding on the JTabbedPane instead, or even closer to the root Window.<br /><br />Also, as could be read out of the JavaDoc rip above, if a component consumes the event, it will not percolate further, which most probably is what you'd want. This means that if the focus is on a JButton, and Space is pressed, this event will be consumed by the button to click it, and the event will not fire any of the WHEN_ANCESTOR_OF_FOCUSED_COMPONENT OR WHEN_IN_FOCUSED_WINDOW Actions bound further up in the hierarchy. Also, if a JTextField is focused, all KEY_TYPED events will be consumed in the same fashion, so that what one writes into the field won't also be processed by Actions higher up in the GUI hierarchy.<br /><br />However, KEY_PRESSED or KEY_RELEASED events will still <b>not be consumed</b> in the manner described above - which when related to the deviousness of KeyStroke's static construction methods can become a little confusing! <i>(As you probably understand by this, it has at least confused me: Java insisted on firing some key bindings I had added for no-modified characters on a component high up in the hierarchy (some JPanel), for example the key 'i' and 't' and so on, even though a text field way lower in the hierarchy was focused and happily processed what I typed - so that when I wrote some text in the field, for example "He liked it!", I also fired off the global actions!)</i><br /><br />It is worth noticing that the older <i>Keymap</i> functionality found on JTextComponents was reimplemented using the InputMap/ActionMap functionality when that was introduced in Java 1.3.<br /><br /><span style="font-weight:bold;">Semantic event firing</span>: To round this off, we'll just do a quick round of the high-level events, or the semantic events as they're also called. <a href="http://java.sun.com/products/jfc/tsc/articles/architecture/">Swing is built upon the <span style="font-style:italic;">Model-View-Controller</span> ("MVC") architecture pattern</a>. The details here are way beyond the scope of this article, but I'll do a quick scratch nevertheless (go <a href="http://en.wikipedia.org/wiki/Model-view-controller">here</a> <a href="http://java.sun.com/developer/onlineTraining/GUI/Swing2/shortcourse.html#JFCMVC">for</a> <a href="http://java.sun.com/developer/technicalArticles/javase/mvc/">more</a>). First of all, the pattern was apparently <a href="http://en.wikipedia.org/wiki/Trygve_Reenskaug">invented by a Norwegian</a> (as is <a href="http://en.wikipedia.org/wiki/Simula">object oriented programming</a> too). Now, with the essentials out of the way: Lets start with a button. The <span style="font-style:italic;">View</span> would the physical/visual button as you see it, while the <span style="font-style:italic;">Model</span> is the button's state ("focused", "armed", "pressed" and clicked or on/off), and the <span style="font-style:italic;">Controller</span> is the logic that transitions the model through these states (getting pinged by the view when the user interacted with it).<br /><br />For Swing, the View and Controller is collapsed into a <span style="font-style:italic;">UI Delegate</span> ("UI"), which forms a part of the Look and Feel ("LaF") system of Swing. This new model is apparently "sometimes referred to" as a <span style="font-style:italic;">Separable Model Architecture</span>. The rationale for this you'll have to pry out yourself from the above-linked documentation - I've never quite understood it..!<br /><br />You thus end up with three classes for a button: the JButton itself (which is an AbstractButton, which is a JComponent, which is a Component), which has a UI delegate of type ButtonUI, and has a model of type ButtonModel. There is no standard ButtonUI, as this is a part of the installed LaF, but there is a "place to start" called BasicButtonUI. The standard ButtonModel is DefaultButtonModel.<ul><br /><li>You instantiate a JButton. Since it is a JComponent and thus a Component, it can have all the low-level AWTEvents fired on it, if it wants.</li><br /><li>The JButton constructor instantiates and sets a DefaultButtonModel, and then (effectively) invokes updateUI, which installs the current LnF-dictated ButtonUI.</li><br /><li>While setting the model, the JButton also installs a bunch of listeners on it. This is so that changes on the model may be reflected physically on the screen by the JButton: If the model becomes pressed, the JButton needs to know so that it can repaint itself into a pressed-looking state.</li><br /><li>Assuming that we're doing BasicUI (which is Okay, WindowsButtonUI extends BasicButtonUI), the UI installs a slew of listeners on the JButton (which, as mentioned, can have all low-level AWT events delivered to it). It does this by instantiating a BasicButtonListener (a helper for the BasicButtonUI) which implement all these listeners, and then installs this instance by invoking the different add*Listener(instance) methods on the JButton.</li><br /><li>The BasicButtonListener obviously implements MouseListener.mousePressed and mouseReleased. On pressed, the BasicButtonListener, i.e. <span style="font-style:italic;">the UI delegate updates the model to be armed and pressed</span>, and it furthermore requests focus to the button. ... </li><br /><li>... while on release, the BasicButtonListener, i.e. <span style="font-style:italic;">the UI delegate updates the model to be un-pressed</span>.</li><br /><li>Since the button was armed, this update to un-pressed state <span style="font-style:italic;">makes the model create and fire the ActionEvent</span>!</li><br /><li>(The BasicButtonListener (i.e. the UI delegate) then also un-arms the model, making it ready for a new round)</li><br /></ul>As a little side-note, if you press the mouse on the JButton, but then drag the mouse out of the JButton while still holding the mouse button, the hooking of an AWTEventListener in the Container to catch mouse dragged events, (as mentioned above on retargeting of MouseEvents) will make sure that the button is still focused and pressed, BUT the still-fired mouseExit event will make the DefaultMouseListener (i.e. the UI delegate) un-arm the model. So, if you now release the mouse button outside of the JButton, it will not fire.<br /><br />Anyways, here's a stack trace for a clicked JButton having an ActionListener installed. I've included the AWT Events preceeding this ActionListener invocation:<br /><br /><span style=";font-family:courier new;font-size:78%;">52729 [AWT-EventQueue-0] AWTEvent: @856d3b java.awt.event.MouseEvent[MOUSE_PRESSED,(57,15),absolute(-305,317),button=1,modifiers=Button1,extModifiers=Button1,clickCount=1] on JButton<br />53995 [AWT-EventQueue-0] AWTEvent: @681db8 java.awt.event.MouseEvent[MOUSE_RELEASED,(57,15),absolute(-305,317),button=1,modifiers=Button1,clickCount=1] on JButton<br />53995 [AWT-EventQueue-0] java.awt.event.ActionEvent[ACTION_PERFORMED,cmd=Button with AL,when=1236222620558,modifiers=Button1] on JButton<br /> com.example.ExampleActionListener.actionPerformed(...)<br /> javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)<br /> javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)<br /> javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387)<br /> ^^ Here we're processing the newly created semantic/high-level ActionEvent<br /> javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:242)<br /> ^^ This invocation is setPressed(false), which, since the model was "pressed" and "armed", creates and fires the ActionEvent.<br /> javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:236)<br /> java.awt.Component.processMouseEvent(Component.java:6216)<br /> javax.swing.JComponent.processMouseEvent(JComponent.java:3265)<br /> java.awt.Component.processEvent(Component.java:5981)<br /> java.awt.Container.processEvent(Container.java:2041)<br /> java.awt.Component.dispatchEventImpl(Component.java:4583)<br /> java.awt.Container.dispatchEventImpl(Container.java:2099)<br /> java.awt.Component.dispatchEvent(Component.java:4413)<br /> ^^ Here we're processing the newly created retargeted AWTEvent, on the JButton instance<br /> java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4556)<br /> java.awt.LightweightDispatcher.processMouseEvent(Container.java:4220)<br /> java.awt.LightweightDispatcher.dispatchEvent(Container.java:4150)<br /> ^^ Invocation of retargeting through LightweightDispatcher<br /> java.awt.Container.dispatchEventImpl(Container.java:2085)<br /> java.awt.Window.dispatchEventImpl(Window.java:2475)<br /> java.awt.Component.dispatchEvent(Component.java:4413)<br /> ^^ Here we're processing the low-level AWTEvent straight from the operating system, on the Window instance<br /> java.awt.EventQueue.dispatchEvent(EventQueue.java:599)<br /> <font color="blue">["EDT pump"]</font><br /></span><br />And with that, we're done!Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com216tag:blogger.com,1999:blog-7596516389741282176.post-83814084613859631452009-02-11T01:16:00.000+01:002009-03-02T16:46:05.654+01:00Swing should be a standalone library<div>The public parts of AWT and Java2D should be Java’s sole interface to the native GUI system. Java2D is already accelerated, and one even have some amount of control of such features with VolatileImage and friends like BufferStrategy. I would personally don't mind more intimate control of and access to the hardware. And I wouldn't be terribly sad if there had to come a Java2D 2 and 3 to handle any new types of GUI hardware, in the same way we have <a href="http://java.sun.com/javase/6/docs/api/java/io/package-summary.html">java.io</a>, <a href="http://en.wikipedia.org/wiki/New_I/O">NIO</a> and <a href="http://jcp.org/en/jsr/detail?id=203">More New IO</a>.</div><div><br /></div><div><a href="http://en.wikipedia.org/wiki/Swing_(Java)">Swing</a> ought to be a standalone, separate, pure java library, built on top of AWT/Java2D. Actually, initially Swing was just that; a library that you had to download on the side, w/o any hidden magic. It was called <a href="http://en.wikipedia.org/wiki/Java_Foundation_Classes">JFC</a>, Java Foundation Classes (to compare against <a href="http://en.wikipedia.org/wiki/Microsoft_Foundation_Class_Library">MFC</a>, Microsoft Foundation Class, a tradition that was upheld with JSP vs ASP and others) and is actually <a href="http://java.sun.com/products/jfc/download.archive.html">still available</a>. With the release of the absurdly named "Java 2" (which gave rise to version names like "Java 2 SDK v1.3.0" and "J2SE v1.4.0"), the JFC was included with the java releases, and the downward spiral of spaghetti was started.</div><div><br /></div><div>Even if one cannot at this point drop the backward compatibility for the current Swing (at least not for a couple of major java versions), one could simply abandon it - and make a refactored Swing2. Here one would drop all the extreme amount of cruft that has accumulated, and fix the problems and annoyances and incorporate the knowledge gained through years since Swing 1 in 1997. The native code of Swing that obviously crept in as long as Swing was a integral part of the JRE, would be ripped out and the necessary parts "ported back" to AWT/Java2D. Importantly, however, this new Swing 2 shoud never be included in the JRE: Applications would have to include it themselves. I know what you immediately think, but this is not as awful, sizewise, as one first might believe. By separating the subsystems themselves into distinct packages (check out <a href="http://openjdk.java.net/projects/jigsaw/">Project Jigsaw</a>), you'd get better code with fewer interdependencies. Swing needs all the separation it can possibly get. By using <a href="http://code.google.com/p/jarjar/">jarjar</a>, only classes used by the application would be included. Possibly <a href="http://proguard.sourceforge.net/">proguard</a> can help shrink the jar size, and <a href="http://en.wikipedia.org/wiki/Pack200">Pack 200</a> shrinks jars by about 60%. You probably already have 10-20 external jars for any little app. Given the enormous potential resulting by separating Swing into an external jar, I'd <span class="Apple-style-span" style="font-weight: bold;">love</span> to depend on this jar too!</div><div><br /></div><div>Swing1 could at some point become an optional element that itself needs download. Given that the JRE by v6u10 goes exactly in this direction, that wouldn't be a problem at all. And I would already look forward to the complete rewrite in Swing3 - or me and some friends would fork Swing2 into Swing2NG or something if we didn’t like the direction Sun's Swing was taking - thereby getting progressive dynamics into the development!</div><div><br /></div><div>On a related note: The project called <a href="http://openjdk.java.net/projects/caciocavallo/">Cacioavallo</a>, proposed <a href="http://mail.openjdk.java.net/pipermail/challenge-discuss/2008-March/000082.html">here</a> by <a href="http://kennke.org/~roman/resume.html">Roman Kennke</a>, is a project which wants "to improve the AWT and Java2D interfaces to enable the implementation of external backends to AWT/Java2D". He (and apparently one more) finished it up, and it is now present in OpenJDK. This is awesome. Furthermore, he followed up by implementing <a href="http://kennke.org/blog/2008/09/03/cacio-swing-awt-peers/">AWT using Swing</a> (all the widgets-parts). This has now <a href="http://kennke.org/blog/2008/12/18/caciocavallo-architecture-overview/">been taken further</a> to the point where to implement the entire "GUI Java", you apparently only need implement a single Graphics2D class onto a direct "pixel plane". All other is thus taken care of. He "proofed" this by making a full implementation using <a href="http://www.directfb.org/">DirectFB</a> (and had already <a href="http://kennke.org/blog/2008/11/04/java2d-on-vxworks/">made a version</a> running on <a href="http://en.wikipedia.org/wiki/VxWorks">VxWorks</a>).</div><div><br /></div><div>Actually, pretty much everything this guy does seems brilliant: <a href="http://kennke.org/blog/">http://kennke.org/blog/</a>.</div><div><br /></div><div>This stuff reminds me of two extremely interesting projects which are similar to each other and yet the complete opposite of each other: <a href="http://swingwt.sourceforge.net/">SwingWT</a> and <a href="http://swtswing.sourceforge.net/">SWTSwing</a>. Too bad those seem dead.</div>Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com4tag:blogger.com,1999:blog-7596516389741282176.post-87581270681836280482009-02-04T18:33:00.000+01:002009-02-18T23:30:01.814+01:00GlazedLists is not "GUI-Lists"!<div><div>This is a post in Praise of GlazedLists - at least the concepts it embodies!</div><div><br /></div><div>It took some time before I "got" GlazedLists.</div><div><br /></div><div>I had read several articles about GL, and went to a JavaOne 2008 talk about it, but I still didn't actually grasp it: I feel that GL's presentations always centers around GUI aspects, in particular list-views and other boring stuff. This was not a good way to sell it to me: I weren't a particularly huge fan of "binding frameworks" and the like. However, I did see something through that GUI-haze, and when I finally did get down into GL, I pretty much immediately rewrote my current project to use it from the core outward! GL's <span class="Apple-style-span" style="font-style: italic;">concepts</span> have <span class="Apple-style-span" style="font-style: italic;">nothing</span> to do with GUI in themselves! It is just that the concepts are really very handy when it comes to several GUI aspects. I now believe GL have utility for any system which have a state consisting of a dynamic set of entities - not only for the "last mile", the actual display to the user, but as a core tool to build ones entity handling on.</div><div><br /></div><div>The main concept of GL is actually not very "big" as such. I'll try to formulate a phrase that would have had me get the concept:</div><div><br /></div><div> " GL is at its core a modifiable List of objects, where sorting (reordering), selections (filtering) and transformations can be applied in a chain, resulting in new list views of the parent list, and the entire chain of lists is kept updated no matter where modification methods are invoked on that chain, by propagating the incremental change events <span class="Apple-style-span" style="font-style: italic;">internal</span> to the chain, not needing an external event structure. "</div><div><br /></div><div>Was that understandable?</div><div><br /></div><div>Think about a system that has a List-of-Customers in the base. You then sort that list based on the customers' last name, getting a new list. Then you filter the list, for example selecting all the customers of age 40 or more. Yes? This resulting list is used often, so you keep a reference to it, "caching" it so to speak.</div><div><br /></div><div>But then you get a new customer.</div><div><br /></div><div><span class="Apple-style-span" style="font-weight: bold;">If you <span class="Apple-style-span" style="font-style: italic;">don't</span> know</span> GlazedLists, you now immediately think about some "NewCustomerEvent" that the container of the base list fires, on which the cache-holder listens, and upon receiving such events, it does the filtering, then sticks it into the list at the correct sorted position. Then you start thinking about customers that want to leave, and realize that you need a "CustomerDeletedEvent". And then, what if the customer was entered with the wrong name or age?</div><div><br /></div><div><span class="Apple-style-span" style="font-weight: bold;">If you <span class="Apple-style-span" style="font-style: italic;">do</span> know</span> GlazedLists, you just smile! :-)</div><div><br /></div><div>The base EventList gets the customer inserted, and sends an internal event up to the listeners, and first hits the SortedList. Here it is inserted at the correct position, but the sortedlist propagates the change further, up to the FilterList, which applies the filter, and now both the intermediate list and the age-40-filtered list is updated, "just like that". You may even change the sort-order, or the filter, and things are always correct.</div><div><br /></div><div>You may also hook onto the event fire system at any point.</div><div><br /></div><div>Now, this happens to be exceptionally useful for GUIs where it can keep different views "magically updated" when the internal state of the system changes, and GL also have lots of such utility classes that handle common "binding cases", but the main concept is useful for pretty much any stateful system that have dynamic set of entities as its state.</div><div><br /></div><div>So, yes: Everyone should check out GlazedLists, not only the die-hard Swing/SWT GUI developers! It is definitely a tool that should be in any serious coder's tool chest blah blah..!</div></div>Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com2tag:blogger.com,1999:blog-7596516389741282176.post-8756601834385270112008-09-08T15:17:00.000+02:002009-03-02T02:45:29.516+01:00Debug Listeners on AWT/Swing componentsBeing a newbie to the whole Swing stuff, I often wonder what events are fired on a given component for any given gesture or event.<br /><br />I wonder no more!<br /><br />Copy the following (ctrl-C), then activate some package node in a project in Eclipse (e.g. the obvious "com.example") and paste (ctrl-V).<br /><br /><pre>import java.awt.Component;<br />import java.lang.reflect.InvocationHandler;<br />import java.lang.reflect.Method;<br />import java.lang.reflect.Proxy;<br />import java.util.ArrayList;<br />import java.util.Arrays;<br />import java.util.List;<br /><br />import javax.swing.JButton;<br /><br />public class DebugStatics {<br /><br /> public interface LineTaker {<br /> void line(String msg);<br /> }<br /><br /> public static final void main(String[] args) {<br /> LineTaker out = new LineTaker() {<br /> @Override<br /> public void line(String msg) {<br /> System.out.println(msg);<br /> }<br /> };<br /> addDebugListeners(out, new JButton(), false);<br /> }<br /><br /> public static void addDebugListeners(final LineTaker out, final Object component,<br /> final boolean includeAllMoveEvents) {<br /><br /> // :: Find all add*Listener methods for supplied component.<br /><br /> List<Class<?>> listenerInterfaces = new ArrayList<Class<?>>();<br /> List<Method> addListenerMethods = new ArrayList<Method>();<br /> Method[] methods = component.getClass().getMethods();<br /> for (Method method : methods) {<br /> String name = method.getName();<br /> if (name.startsWith("add") && name.endsWith("Listener")) {<br /> Class<?>[] parameters = method.getParameterTypes();<br /> if (parameters.length == 1) {<br /> Class<?> interfaze = parameters[0];<br /> if (interfaze.isInterface()) {<br /> listenerInterfaces.add(interfaze);<br /> addListenerMethods.add(method);<br /> }<br /> }<br /> }<br /> }<br /><br /> // :: Make handler for "super listener"<br /><br /> InvocationHandler handler = new InvocationHandler() {<br /> long _lastEvent;<br /> String _lastMethodName;<br /> boolean _threadFired;<br /> InvocationHandler _handler = this;<br /><br /> @Override<br /> public synchronized Object invoke(@SuppressWarnings ("unused") Object proxy, Method method, Object[] args)<br /> throws Throwable {<br /> _lastEvent = System.currentTimeMillis();<br /> String methodName = method.getName();<br /> if (includeAllMoveEvents || !(methodName.endsWith("Moved") && methodName.equals(_lastMethodName))) {<br /> out.line("event:[" + method.getName() + "] - on - [" + method.getDeclaringClass().getName()<br /> + "] - with - " + Arrays.asList(args) + ".");<br /> if (!_threadFired) {<br /> _threadFired = true;<br /> new Thread("debug:Event Stream Breaker") {<br /> @Override<br /> public void run() {<br /> while (true) {<br /> long millisLeft;<br /> synchronized (_handler) {<br /> millisLeft = 400 - (System.currentTimeMillis() - _lastEvent);<br /> if (millisLeft < 0) {<br /> out.line("===== event stream break.");<br /> _threadFired = false;<br /> _lastMethodName = null;<br /> break;<br /> }<br /> }<br /> try {<br /> Thread.sleep(millisLeft + 2);<br /> continue;<br /> }<br /> catch (InterruptedException e) {<br /> break;<br /> }<br /> }<br /> }<br /> }.start();<br /> }<br /> }<br /> _lastMethodName = methodName;<br /> return null;<br /> }<br /> };<br /><br /> // :: Make "super listener", "implementing" all the Listener interfaces<br /><br /> Object superListener = Proxy.newProxyInstance(DebugStatics.class.getClassLoader(), listenerInterfaces<br /> .toArray(new Class<?>[0]), handler);<br /><br /> // :: Attach "super listener" using all add*Listener methods on supplied component<br /><br /> for (Method method : addListenerMethods) {<br /> try {<br /> method.invoke(component, superListener);<br /> out.line(" ++ add*Listener: [" + method + "].");<br /> }<br /> catch (Throwable e) {<br /> out.line("Got error when trying to invoke add*Listener method:[" + method + "]." + e);<br /> }<br /> }<br /> }<br />}<br /></pre>Code discussion: The <code>LineTaker</code> comes from the need to have some flexibility regarding where the textual stream of events should end up (e.g. to <code>System.out.println(...)</code> or to some <code>logger.debug(...)</code>). The driver (<code>main</code> method) is just to check out the listener adding (and possibly also to find out which types of add*Listener methods a given AWT/Swing/Whatever class has). The reason for the <code>Thread</code>-forking is so that you more easily can understand which events "belongs" to each actual, physical gesture: Move the mouse into a button, wait a second, then press down mouse button, wait a second, release mouse button, wait a second, move out. Now the result event stream have embedded a break on every second-wait. The thread only lives long enough to write the break, then exits. The reason for the code regarding move-events suppression is that you get an awful lot of move-events without it - this code results in only one move event being recorded in a consecutive run of the same move event.<br /><br />While dumping the code into this post, it hit me that the above method actually works for any class having <code>add*Listener(AnyInterface listener)</code> methods. That's why the type of the <code>component</code> argument is <code>Object</code>.<br /><br />There's a pretty impressive amount of events being fired on a JButton for the simple act of using the mouse to click it!Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com0tag:blogger.com,1999:blog-7596516389741282176.post-47221067274547380592008-08-21T19:03:00.002+02:002009-03-20T15:12:56.519+01:00Subversive svn: authentication cancelledI just wanted to record this for anyone that gets a problem that I had, as I didn't find the solution "by the way of Google". Do note that I'm no expert on either Subversion or Subversive..!<br /><br /><b><font color="red">Before doing anything described here, please back up your workspace!</font> I'm not talking about fetching that monster tape drive, install and run some huge backup software and then ship the tapes off to another continent - just make a copy somwhere of your Eclipse workspace. This so that you can start over, trying another angle, <i>when</i> things doesn't work out and your whole workspace is accidentially encrypted using your lavalamp's current formation as the key! (You then shut down Eclipse, delete your workspace, and "restore" the backup by copying it back. Start eclipse, and try your new approach.)</b><br /><br /><i>Before going further, first just attempt to start Eclipse with the "-clean" argument. You should not even need to worry about that backup then - and it <b>might</b> fix everything.</i><br /><br />I have recently upgraded Eclipse to 3.4.0. I backed up my Workspace to some temp space, and then just pointed the new Eclipse to the same Workspace (the existing, "old" copy..). This magically worked nearly flawlessly: The Workbench looked exactly the same, all views set up the same, all Mylyn local tasks (which was the only type I had) was set up with contexts and everything.<br /><br />But there were apparently some problems. The first one I don't <span style="font-style: italic;">quite</span> remember, but Eclipse/Subversive at some point asked me if I wanted to reconnect to "the now-deleted repository location" something "https://blahabla...". And I then also noticed that all the Subversive markings (where you are (trunk), version numbers, colours, markings, whatevers you've configured Subversive to adorn your project with) were gone. I just said yes, and hoped for the best. Nothing happened. But after a reboot of Eclipse (I've found that much fixes itself by booting this application!), everything seemed to work.<br /><br />But today, I wanted to commit something. This didn't work out at all, and in the Console window, I got "svn: authentication cancelled" and "blahblah Error". The annoying thing was that it just never asked me for credentials, so I couldn't give it my password (the username was saved, at least earlier). When this happened, Eclipse popped up a screen stating that some error had occurred (I don't remember the wording here, it was terse and useless), stopping up.<br /><br />So, I did a couple of things which apparently seemed to work:<br /><ul><li>Deleted "C:\Documents and Settings\<username>\Application Data\Subversion", based on <a href="http://forums.oracle.com/forums/thread.jspa?threadID=640909&tstart=0">this</a> Oracle bug. You'll then be asked whether the certificate is correct etc etc upon next attempt to do Team->Update. Do note, however, that I don't think this is a part of the solution! So you may go ahead without doing this, at least the first round..</li><li>Back up the project in question, in particular if you have heaps of uncommitted stash there! (If you don't have uncommitted data, you might just as well delete the entire project, and just start with a fresh checkout).<br /></li><li>Right-click on the problematic project -> Team -> Disconnect</li><li>Choose "Do not delete the SVN meta-information (e.g. .svn subdirectories)", click Yes.</li><li>After a while, you'll see that the project disconnects, and the Subversive markings disappears.</li><li>Now, you go Right-click -> Team (Don't freak out by this menu being pretty much empty!) -> Share Project...</li><li>Pick SVN (Obviously), click Next<br /></li><li>Pick the "Create location using project settings" (second choice here).</li><li>You'll come to the "Enter Repository Location Information" page.</li><li>At this point, the one thing that I immediately noticed, was that it suggested URL "https://blahblah/svn/<projectname>/<span style="font-style: italic;">trunk</span>". I don't think the "trunk" part shall be a part of the location, so I removed it.<br /></li><li>Check the "Validate Repository Location on finish".</li><li>Enter your username and password. I don't want to store the password, so I leave "Save password" unchecked.</li><li>You may at that point click the "Browse" button to check if your URL is good - it should show the "branches", "tags", "trunk" directories.</li><li>On the Advanced tab, check "Enable Structure Detection".</li><li>I don't have anything fancy for the SSH Settings nor SSL Settings.</li><li>When I clicked Finish, the Subversive markings reappeared, everything worked, and I could update and commit right away!</li><li>Note that the repository location, which you may enable the project name node to be adorned with by Subversive, reads "https://blahblah/svn/<projectname>", leaving out the "trunk" part. The full text on my project name node is "[https://blahblah/svn/<projectname>: trunk]" - but this isn't standard config for Subversive, I believe.</li></ul>The "trunk" in the URL stuff is actually what I think might have gone wrong the first time around with the first problem described above, where I just accepted what Eclipse suggested to me as solution. So this is furthermore what I suspect has been the reason behind the whole authentication cancelled thing: it really had the wrong URL, but the Subversive client handles this aspect (as it still points to the same place: either, wrongly, "trunk" on the URL and "" on the branch-selection, or more correctly, sans "trunk" on the url, but "trunk" on the branch-selection), but don't any longer know how to handle authentication..<br /><br />Actually, I have ended up with something rather like this once before: I didn't know what URL Subversive wanted, so I navigated using the browse button, and navigated all the way into the "trunk" directory, as it was trunk I wanted to check out and work with, and I didn't immediately see any other way to select that. Subversive is seemingly quite happy with this, checking out your project and whatnot. But then, later in the game, things start to weird out. I don't quite remember how this manifested itself, but I do remember that I got some strange recursion problem, where I could see the entire trunk/branch/tags structure within my project. When I realized the URL thing, deleted the project and started anew, everything cleared up.<br /><br />I think Subversive should be nice enough to point out that you've <span style="font-weight: bold;">most probably</span> navigated wrongly now, as the latter part of the SVN URL is at this point looks like a "known structural ending", and this should not be a part of the URL blah blah. See, Subversive does know what the standard structure of a SVN setup is - so it could easily have done this. I filed a bug about this (a year or so ago), but so far nothing have happened.<br /><br /><i>Update 2008-09-08</i>: I got the same problem again some days ago. Subversive doesn't use the normal Preferences screen to define subversion repositories. It has its own view, called "SVN Repositories" (in the same way that Mylyn has "Task Repositories"). Here you can find the different repositories. Right click, and select "Location Properties..." (Not "Show properties", as that refers to subversion properties; Those things that you can stick on directories and files within the repo, e.g. "svn:ignore" @ trunk). Hitting the Location Properties, you get that Edit Repository Location dialog which I refer to in the above hundred or so bullets. If you here click the Browse button, you will get a username/password dialog (given that they aren't correct on the parent dialog). You should be able to browse the repository. When this works, stick in the username/password in the Edit Repository Location dialog again, tick off "Validate Repository Location on Finish", and hit the "Finish" button. The authentication problem is now gone.<br /><br />In addition, I believe I might still be mistaken about what URL the location should be. I have now changed it to not include the <projectname> part as I stated above, as the URL without that part would be the location to the actual Subversion repository. This seems more logical (and it still works!).<br /><br />Furthermore, after a couple of those "svn: authentication cancelled" rounds, I tried starting eclipse with the "-clean" argument. I have not had the problem afterwards. Whether it was the location URL, or the -clean that finally fixed it, I am not sure about, although I tend to believe the latter.<br /><br />Finally, it is actually also possible to completely disconnect from the repository (Team->Disconnect), choosing "Also delete the SVN meta-information from the file system" from the Confirm Disconnect from SVN dialog. You can then Team->Share, make a new repository location, and from that pick the same project you have the files for. Subversive apparently realizes that the project name you try to share it as, already has information in it, and apparently checks it out at a temp location, then copy back the files, seemingly keeping your changes. I would however not bet on that - this really seems just as risky as to start afresh with a new checkout of the project. In any case here, back up your workspace before trying anything!!Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com4tag:blogger.com,1999:blog-7596516389741282176.post-7172330014801170572008-03-05T15:23:00.000+01:002008-03-08T15:08:38.341+01:00Debugging and Profiling - same, not differentWhy is it that Eclipse has such a great debugger, but such a extremely poor profiler?<br /><br />I've used JProfiler and YourKit Profiler, and find both products interesting (however, JProfiler gets my money). However, they are a just that slight tad too awkward to fire up - I don't use them often enough, so it is not "in the fingers" in the same way I <b>always</b> run my programs through the Eclipse debugger.<br /><br />The Eclipse profiler, when I've gotten it to work, which is a real PITA, is amazingly slow - I've never been able to profile even the simplest applications. I wonder if it doesn't use some XML format to transfer the profiling information from the profiled application to the profiler..<br /><br />The Eclipse debugger, on the other hand, is a truly wonderful tool - for what it is made for.<br /><br />Then, one suddenly starts to wonder: aren't these two things pretty much the same stuff? Both the debugger and the profiler needs to be able to inspect the objects on heap, control threads, inspect stacks. This has been recognized by the JVM vendors, where the two JVM interfaces, JVMPI (JVM Profiling Interface) and JVMDI (JVM Debugger Interface), have been unified into the JVMTI (JVM Tools Interface). So why aren't simply these two JVM tools indeed a unified power tool?<br /><br />I've <b>so many times</b> wanted to just stop the application and check out what all the memory is used for right now - "Heap Walking" in JProfiler terminology. Or just do a quick analyze of where the CPU is being used from here on to there on, using for example two breakpoints. There are plenty other scenarios where the features of a debugger and a profiler overlap, and where just one of them doesn't quite cut it. Starting the JVM with both JProfiler and Eclipse attached actually seems to work in some fashion (as long as you don't pause JProfiler's handling threads, as the JProfiler GUI then freezes), but I always seem to get Threads in the debugger that when paused, don't give me the stack, but instead show "<Not responding>", causing Eclipse to pretty much freeze too, and it is hardly the perfect situation at any rate.<br /><br />Here's some possibilities:<br /><ul><br /> <li>Take a heap snapshot at some certain breakpoint.</li><br /> <li>Pausing the application, then taking a "heap walking tour". Resume, then exercise the application by doing that thing that always messes up. Pause again, snapshot, compare difference (or use breakpoints instead of manual pauses).</li><br /> <li>Stepping the application, having one thread in pause. Make a heap snapshot of all objects that the current thread's stack points to, displaying it graphically along with the stack trace, arrows pointing out from each stack element.</li><br /> <li>.. and being able to expand a Thread's stack elements and view the actual variables that hold on to each part of the heap graph, visually using arrows pointing to the objects</li><br /> <li>When in "heap walking", have a more direct understanding of what threads hold on to what objects.</li><br /> <li>Start allocation recording and/or cpu recording (start profiling) when any thread reaches some breakpoint.</li><br /> <li>Do timing analysis (profiling) of a single thread between two breakpoints. Do this repeatedly while the application is being exercised, and aggregate.</li><br /> <li>By default pause the application at a OutOfMemoryError and do a heap snapshot.</li><br /> <li>(Admittedly only a profiling wish:) Like YourKit (unlike JProfiler), let heap snapshots be "full" (include the data) and standalone, with thread stack information, in that the entire heap is dumped to disk, so that it is possible to easily compare.</li><br /></ul><br />Basically - what I want is the <b>integration</b> of the features that a debugger has, and what a profiler has. In particular the combination of stack-inspection from the debugger with heap-inspection from the profiler, and the code-integrated features with breakpoints and exception-breakpoints that a debugger has (the now standard fully integrated IDE debugger), triggering the profiling and heap snapshotting features that a profiler typically has.<br /><br />I think that if these JVM inspection tools wasn't made with one or the other application in mind but instead thought of as one unified unit, a much more powerful tool would emerge. (I nearly get that tech-drool when thinking of how cool it would be: "so, I believe this object should go out of scope at <b>this</b> breakpoint.. So, why isn't it GC-able now? Lets trace-to its GC roots..").<br /><br />Now, I have understood that Netbeans have a much more integrated profiler than Eclipse. I really should check Netbeans out someday very soon..!Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com5tag:blogger.com,1999:blog-7596516389741282176.post-23659239488197299462008-02-25T11:12:00.001+01:002009-03-03T23:32:28.677+01:00Too much verbosity in generics!<span style="font-weight: bold;">Why</span> do I have to write the following bold part:<br /><span style="font-family:courier new;">Map<SomeLongClass, SomeOtherThing<Another>> myPoint = new HashMap<span style="font-weight: bold;"><SomeLongClass, SomeOtherThing<Another>></span>();</span><br /><br />(Update 2009-03-03: As you probably already knew, <a href="http://tech.puredanger.com/java7#typeinference">I am not the only one realizing this</a>!)<br /><br />As far as I know, anything else than a verbatim copy of the stash that already is present in the declaration of that variable/field is a compile error. So why do I have to type it?? Argh..!<br /><br />And OH so annoying Eclipse behaves if you haven't yet imported Map while writing that line - because if you've come to the point where you're about to write "Hash.." and then think that it would be reeeally nice if Eclipse could just autocomplete the rest of that shit for you, it doesn't quite work! And at that point it doesn't help to <span style="font-style: italic;">arrange imports</span> either, because the code is currently to uncompilable for poor Eclipse to even understand what you're aiming at, so you'll have to delete up to the equals-sign, stash in a semicolon, then arrange imports, and finally start up with the "Hash..." again, now having autocomplete functionality at the ready. Grr..<br /><br />Please, someone: I'd like Java itself to remove the need for that duplicated stupidity, and then Eclipse to be better at trying to compile partially if it doesn't understand the line the cursor is on (so that at least arrange imports would work at the described annoyance), and finally, I'd like some extra-super context-assist for writing the line in the first place - letting Eclipse know of the standard implementations of all Collections, and also picking up the classpath. For that matter, it could do such stuff all the time.<br /><br />(And while I'm whining: Blogger doesn't handle <'s and >'s in "rich text" mode..)Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com0tag:blogger.com,1999:blog-7596516389741282176.post-57045369502539700662008-02-21T10:34:00.000+01:002008-02-21T11:00:53.176+01:00Java Garbage Collection details - "Garbage First" G1For folks into the details of java, there's an interesting blog entry called "Our Collectors" over at Jon Masamitsu's blog, who apparently is a Sun GC guru. It has a nice low-down over the different collectors - what they are, where they are used, and how they can be combined.<br /><br />Also, this is a good place for some insight into the G1 GC - "Garbage First", a new algorithm that is going to be the default in Java 7.<br /><br />Some interesting discussion ensues, where Tony Printezis, apparently another Sun insider, chips in with comments.<br /><br /><a href="http://blogs.sun.com/jonthecollector/entry/our_collectors">http://blogs.sun.com/jonthecollector/entry/our_collectors</a><br /><br />Also, don't miss this good whitepaper, <a href="http://java.sun.com/j2se/reference/whitepapers/memorymanagement_whitepaper.pdf">Memory Management in the Java HotSpot™ Virtual Machine</a> (pdf, April 2006).Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com0tag:blogger.com,1999:blog-7596516389741282176.post-8374193726932864002008-02-15T01:51:00.000+01:002008-02-21T10:58:44.881+01:00Linux RAID1: Failing disk in boot set, part 1I'm running software RAID on Linux, using the md module.<br /><br />I have everything RAIDed, including /boot and swap.<br /><br />The /boot and / is on a RAID1 set. The logic behind that is the boostrap problem: the kernel must be loaded to enable the md_mod module and hence enable RAID, but the kernel resides on the RAID set - catch 22! However, since RAID1 is pure mirroring, things work out: assume the boot disk isn't a part of a RAID set while loading the kernel from /dev/sda1 in read-only mode, then, when it comes up, load and activate the RAID module, enable the RAID set, and continue taking up the system using the now /dev/md0 device.<br /><br />So, if a disk fails, find out which one (find its serial number), chuck that out, stick in a new one, and then boot up. The RAID module will upon trying to assemble the RAID sets discover that one of the device is gone, and simply take up the set in <span style="font-style: italic;">reduced mode</span>. You are then able to partition your new disk exactly as the one already present, and then add the new device back into the RAID set.<br /><br />Simple!<br /><br /><span style="font-weight: bold;">However</span>, there is a bootstrap problem before the one described, which is what really is called "the boot process": When the computer starts up, it reads the first sector of the boot harddisk (typically the first in its discovery sequence, possibly changeable in the BIOS setup) into memory, and runs that. This is where the <span style="font-style: italic;">bootloader</span> resides.<br /><br />The only problem is that a normal system only sticks the bootloader on the mentioned boot harddisk. This part of the disk isn't handled by the RAID module - so it won't be copied over to the other disk (e.g. the partition table is there too - can't have two different sized disk's partition table hard-copied from one to the other).<br /><br />So, if a disk fails, you'd better hope that it is the <span style="font-style: italic;">other</span> disk, not the boot disk!<br /><br />I wasn't that lucky...!Endre Stølsvikhttp://www.blogger.com/profile/01628989297352831420noreply@blogger.com0