Azureus's stunning visualizations (Vuze)
Posted by Simon on July 18, 2008 at 07:51 PM
In order to get around Bell Sympatico's bittorrent throttling I recently switched to Azureus (aka Vuze). If you switch to the "classic" UI mode, it has some stunning visualizations of what's happening with your torrents.
The main screen contains a bit more information than you might need, but if you play with the columns that are visible (right click on the headers) you can get something like this:

What you've got there is downloading torrents at the top and finished ones at the bottom. Green happy faces are currently in progress. Gray ones are queued. In the bottom right corner you can see that my total download speed is 311 kilobytes per second, and total upload is 50kB/s (I'm on ADSL).

Suppose I want to zoom in on one particular torrent — double click on it. This shows each of the peers I'm connected to. What pieces of the file do they have? How far complete are they in total? Bittorrent downloads files in chunks and it does the chunks randomly, not from start to end, so this information can be interesting.

The above shows me EVEN MORE details if I really want it (OK, some of this stuff is really excessive). It shows which of the pieces I've got (blue) and which ones are downloading (in red). Just in case you wanted to know...

Swarm (above) is an actual animation of the pieces of the file as each of your peers around the edges send the bits to you in the middle. And it also shows the reverse as well. And the pie charts show how much of the torrent each peer has. Wild stuff.
So, that's if you want to know what's happening with one particular torrent. But what if you want to know about your overall connection with all the different peers and torrents? Well, Azureus gives loads of graphs and charts for that as well.
This one is your overall bandwidth monitor:

Nice. I love staring at this one. It's a really good example about how to cleanly show multiple related variables in a time-based chart (aka histogram). For the top one, the blue filled area is your download speed. Really interesting is the gray line, which is the average download speed of the SWARM. In other words, what is your average peer getting? If you're below this line, then you're getting screwed — or there's something wrong with your configuration. If you're above it, you're doing well. It's a good way to get a quick fix on the health of your downloads as compared to other users. It also makes it really easy to see if you're being rate-limited by your ISP.
On the bottom half, you can see that I've enabled Auto-Speed and it's automatically cranking the max upload speed up and down based on measuring my bandwidth and other factors that I'm not too clear on.
There's other visualizations but those are my favourites. Some of them aren't really documented and I don't really understand exactly what they mean (transfers and vivaldi for example). Still, obviously one of the azureus open source developers is a data viz keener and s/he's done some fine work.
Hacking the java compiler: using anonymous subclasses as closures
Posted by Simon on July 10, 2008 at 11:17 PM
In Java, closures/first-order functions are not a language feature. However, as everyone knows, you can effectively get a first-order function by using an anonymous subclass instead. Something like this:
class MyClosure {
void run() {} // override this
}
void doSomethingClosureLike() {
MyClosure closure = new MyClosure() { void run() { System.out.println("We're inside a closure!"); }};
runTheClosure(closure);
}
void runTheClosure(MyClosure closure) {
closure.run();
}
// will print We're inside a closure!
Anyway, it's simple enough, you pass the class instead of the function and there's a little extra verbage but it works!
Also you get closure-like functionality, because inside run() you can access variables from outwhere where you created it. E.g.:
void doSomethingCooler() {
final String myString = "Foo!";
MyClosure closure = new MyClosure() { void run() { System.out.println("The string is: " + myString); }};
runTheClosure(closure);
}
// will print The string is: Foo!
You can also access global variables that change over time, and the closure will use whatever is the current value WHEN THE CLOSURE RUNS.
There's just one small annoying thing, which is this particularly annoying compiler message:
local variable (WHATEVER) is accessed from within inner class; needs to be declared final
If you were do change myString to not be final, you'd get that error. Bummer. You could make myString a global variable and that would work, but that's stupid. There is a better way. Try this:
void doSomethingCooler() {
String myString = "Foo!";
final String myStringFinal = myString;
myString.concat(" Bar!");
MyClosure closure = new MyClosure() { void run() { System.out.println("The string is:" + myStringFinal); }};
runTheClosure(closure);
}
// will print Foo! Bar!Now you can even change myString after you assign myStringFinal, because Java, although they say it doesn't use pointers, really does use pointers. I.e. it passes by reference. So, myStringFinal is actually just a reference to myString, and keeps pointing to it even when you change the contents of myString.
You can CHANGE it (like using concat()) but you CAN'T reassign it. That will break the pointers. It makes sense if you think about it—myString will have a new memory address, and myStringFinal will still be pointing to the old memory address (and the old string value). So, this won't work:
myString = "won't work"; // breaks myStringFinal
You can use this technique with any object (but not primitives like int).
ALL NEW "Simon Says" content RIGHT HERE
Posted by Simon on July 10, 2008 at 02:52 PM
Wow, WYM Editor is so cool that I can just like type in a new blog post whenever I want to. Wild!
So anyway, I've been saving up a whole load of links and stuff for months until I had this new site all sorted out. So here's something.
Hmm... where did my "stuff to blog about" folder go?
Oh, here's an awesome one. Nikkei Electronics Teardown Squad. These guys kick ass. Watch as they take apart a MacBook Air and declare "No Waste Outside, Nothing but Waste Inside".
About 30 screws were used to attach the keyboard alone. "The total number of screws in the MacBook Air was several times the number used in a PC we make," one of the engineers said.
Burn, baby, burn!
OK, here's another one from the files. Nathan Fawkes Art. He's part of a network of film animators and illustrators and concept artists who all have their stuff up on blogspot.
And I'd like to remind myself particularly about this post about science fiction.
Restoring the old posts
Posted by Simon on July 07, 2008 at 12:35 AM
OK, here's a test of how WYMeditor works, because I'm going to try to copy/paste some code in here. I just had a little foray into my past with XSLT. I had 344 old blog posts (starting year 2000!) to convert from XML to SQL. Nothing better than XSL for the job! Here it is.
NB: I haven't restored images as of this writing.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="no" omit-xml-declaration="yes" encoding="ASCII"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="ucletters">ABCDEFGHIJKLMNOPQRSTUVWXYZ- </xsl:variable>
<xsl:variable name="lcletters">abcdefghijklmnopqrstuvwxyz-_</xsl:variable>
<xsl:variable name="allowed_letters">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_ </xsl:variable>
<xsl:template match="/">
<xsl:text> USE `sw-blog-dev`;
</xsl:text>
<xsl:apply-templates select="weblog/entry"/>
</xsl:template>
<xsl:template match="entry">
<xsl:text>INSERT INTO `sw-blog-dev`.`posts` (`author_id`,`created_at`,`modified_at`,`permalink`,`title`,`synd_title`,`summary`,`body_raw`,`extended_raw`,`body`,`extended`,`is_active`,`custom_field_1`,`custom_field_2`,`custom_field_3`,`body_searchable`,`extended_searchable`,`text_filter`,`comment_status`) VALUES
</xsl:text>
<xsl:text> (2,</xsl:text> <!— author_id —>
<xsl:text>'</xsl:text><xsl:apply-templates select="date"/><xsl:text> 12:00:00',</xsl:text> <!— created_at —>
<xsl:text>'</xsl:text><xsl:apply-templates select="date"/><xsl:text> 12:00:00',</xsl:text> <!— modified_at —>
<xsl:text>'</xsl:text><xsl:apply-templates mode="PERMALINK" select="title"/><xsl:text>',</xsl:text><!— permalink —>
<xsl:text>'</xsl:text><xsl:apply-templates mode="XHTML" select="title/text()"/><xsl:text>',</xsl:text><!— title —>
<xsl:text>'</xsl:text><xsl:apply-templates mode="SYND_TITLE" select="content"/><xsl:text>',</xsl:text><!— synd_title —>
<xsl:text>'',</xsl:text><!— summary —>
<xsl:text>'</xsl:text><xsl:apply-templates mode="XHTML" select="content"/><xsl:text>',</xsl:text><!— body_raw —>
<xsl:text>'',</xsl:text><!— extended_raw —>
<xsl:text>'</xsl:text><xsl:apply-templates mode="XHTML" select="content"/><xsl:text>',</xsl:text><!— body —>
<xsl:text>'',</xsl:text><!— extended —>
<xsl:text>1,</xsl:text><!— is_active —>
<xsl:text>'',</xsl:text> <!— custom_field_1 —>
<xsl:text>'',</xsl:text> <!— custom_field_2 —>
<xsl:text>'',</xsl:text> <!— custom_field_3 —>
<xsl:text>'</xsl:text><xsl:apply-templates mode="TEXT_ONLY" select="content"/><xsl:text>',</xsl:text><!— body_searchable —>
<xsl:text>'',</xsl:text><!— extended_searchable —>
<xsl:text>'markdown',</xsl:text><!— text_filter —>
<xsl:text>1);
</xsl:text><!— comment_status —>
</xsl:template>
<!— must remember to backslash all single quotes —>
<xsl:template match="date">
<xsl:value-of select="translate(.,'/','-')" />
</xsl:template>
<xsl:template mode="PERMALINK" match="title">
<xsl:value-of select="substring(
translate(
translate(., translate(., $allowed_letters, ''), ''),
$ucletters,
$lcletters
),
0,42)"/>
</xsl:template>
<xsl:template mode="SYND_TITLE" match="content">
<xsl:call-template name="escapesinglequotes">
<xsl:with-param name="arg1"><xsl:value-of select="normalize-space( substring(.,0,42) )"/></xsl:with-param>
</xsl:call-template>
<xsl:text>...</xsl:text>
</xsl:template>
<xsl:template mode="TEXT_ONLY" match="content">
<xsl:call-template name="escapesinglequotes">
<xsl:with-param name="arg1"><xsl:value-of select="normalize-space(.)"/></xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template mode="XHTML" match="content">
<xsl:apply-templates mode="XHTML"/>
</xsl:template>
<xsl:template mode="XHTML" match="node()|@*">
<xsl:copy>
<xsl:apply-templates mode="XHTML" select="@*"/>
<xsl:apply-templates mode="XHTML"/>
</xsl:copy>
</xsl:template>
<xsl:template mode="XHTML" match="text()">
<xsl:call-template name="escapesinglequotes">
<xsl:with-param name="arg1"><xsl:value-of select="normalize-space(.)"/></xsl:with-param>
</xsl:call-template>
<xsl:text> </xsl:text>
</xsl:template>
<xsl:template mode="XHTML" match="@*">
<xsl:attribute name="{name()}">
<xsl:call-template name="escapesinglequotes">
<xsl:with-param name="arg1"><xsl:value-of select="normalize-space(.)"/></xsl:with-param>
</xsl:call-template>
</xsl:attribute>
</xsl:template>
<xsl:template name="escapesinglequotes">
<xsl:param name="arg1"/>
<xsl:variable name="apostrophe">'</xsl:variable>
<xsl:choose>
<!— this string has at least on single quote —>
<xsl:when test="contains($arg1, $apostrophe)">
<xsl:if test="string-length(normalize-space(substring-before($arg1, $apostrophe))) > 0"><xsl:value-of select="substring-before($arg1, $apostrophe)" disable-output-escaping="yes"/>\'</xsl:if>
<xsl:call-template name="escapesinglequotes">
<xsl:with-param name="arg1"><xsl:value-of select="substring-after($arg1, $apostrophe)" disable-output-escaping="yes"/></xsl:with-param>
</xsl:call-template>
</xsl:when>
<!— no quotes found in string, just print it —>
<xsl:when test="string-length(normalize-space($arg1)) > 0"><xsl:value-of select="normalize-space($arg1)"/></xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Niiiiiiice.
Blogging on Rails
Posted by Simon on July 07, 2008 at 12:00 AM
Hi there.
Well, I'm back. I was running this site on really ancient technology — AxKit — so 2001. Now I'm running it on modern technology, i.e. Rails 2. And doesn't it rock. Now I have a cool GUI editor to type into, I have easy programming in ruby, and I have of course polished both my design and my CSS/XHTML skillz considerably in the mean time, hopefully making this all easier to look at and navigate.
So I'm running on SimpleLog here, but it's not "stock". Oh no. Stock SimpleLog right doesn't run on Rails 2, but this one does. Also, I made it even MORE simple than it used to be:
- Support Rails 2.0 (no need to freeze an old rails)
- no themes—annoying to use anyway, and no one was publishing themes either
- replaced the editor/preview panel with WYM on Rails, which is by FAR the best WYSIWYG / GUI editor I've ever found, and the end of a long search for me
...and so on.