User interface responsiveness is a crucial thing for all applications that require user interaction, but maybe even more so when programming for mobile handsets. Google’s Android programming environment unfortunately does not provide any mechanism for handling operations that may require a fair amount of time to complete, but which in itself are not meant to be implemented as Android Services. An example for this would be network I/O in an Activity, such as posting data to (or retrieving data from) a remote Web server. Because the Android runtime will terminate any Activity that does not respond within a couple of seconds, it is impossible (and simply a bad idea anyway) to perform such tasks from within the UI main thread.

That being said, I have come up with a generic class that handles long running operations by spawning a separate thread and passing back any result or error data to the main thread using a callback mechanism. An animated progress dialog will be displayed while the operation is running. That way the user is kept informed about any program activity that may take some time to complete.

The class can be used as follows:


public class MyActivity implements LongRunningActionCallback<Void> {

    private LongRunningActionDispatcher<Void> dispatcher;

    private void startLongRunningOperation() {
        // the first argument is a reference to the current Context, in this
        // case the current Activity. The second argument is a reference to
        // the object implementing the callback method.
        this.dispatcher = new LongRunningActionDispatcher<Void>(this, this);
        dispatcher.startLongRunningAction(new Callable<Void>() {
            public Void call() throws Exception {
                // perform your actions that take a long time
                return null;
            }
        }, "Dialog Title", "Dialog message");
    }

    // the callback
    public void onLongRunningActionFinished(Void result, Exception error) {
        if (error != null) {
            // handle error
        } else {
            // success, work with the result, if any
        }
    }
}

This will spawn a progress dialog (not indicating any actual progress in percentage, it’s just a “busy” dialog) with the given title and message. If an exception occurred in the Callable you provided, it will be passed as the error argument to the callback, so you should always check whether it’s non-null.

Below are the source codes for both LongRunningActionDispatcher and LongRunningActionCallback.


import java.util.concurrent.Callable;

import android.app.ProgressDialog;
import android.content.Context;
import android.os.Handler;
import android.util.Log;

/**
 * Use this class if you need to dispatch expensive (long running) operations
 * from your Activity. The long running operation is provided to
 * {@link startLongRunningAction} as a {@link Callable}. The result of the
 * operation and any potential exception that occurred during the call are
 * passed to {@link LongRunningActionCallback.onLongRunningActionFinished},
 * which will be called on successful or unsuccessful completion of the
 * Callable.
 *
 * LICENSE STATEMENT (DO NOT REMOVE):
 * This code is in the public domain. You may use, alter, and redistribute it
 * free of any charges or obligations, with the following exceptions:
 * 1. You are not allowed to remove the statement naming the original author.
 * 2. You are not allowed to remove this license statement.
 *
 * @author Matthias Kaeppler
 */
public final class LongRunningActionDispatcher<ResultType> {

    private Context context;

    private LongRunningActionCallback<ResultType> callback;

    /**
     * A progress dialog shown during long-lasting operations
     */
    private ProgressDialog progressDialog;

    private Handler finishedHandler = new Handler();

    public LongRunningActionDispatcher(Context context,
            LongRunningActionCallback<ResultType> callback) {
        this.context = context;
        this.callback = callback;
    }

    /**
     * Invoke this method to start long running operations which may block your
     * activity and therefore the main UI thread. A progress dialog will be
     * shown while the operation is executing.
     *
     * @param callable
     *            The callable
     * @param progressDialogTitle
     *            The progress dialog title
     * @param progressDialogMessage
     *            The progress dialog message
     */
    public void startLongRunningAction(final Callable<ResultType> callable,
            String progressDialogTitle, String progressDialogMessage) {

        progressDialog = ProgressDialog.show(context, progressDialogTitle,
                progressDialogMessage, true, false);

        new Thread(new Runnable() {

            public void run() {
                ResultType result = null;
                Exception error = null;
                try {
                    result = callable.call();
                } catch (Exception e) {
                    Log.e("ERROR", e.getMessage());
                    error = e;
                }

                final ResultType finalResult = result;
                final Exception finalError = error;
                finishedHandler.post(new Runnable() {

                    public void run() {
                        onLongRunningActionFinished(finalResult, finalError);
                    }
                });
            }
        }).start();
    }

    private void onLongRunningActionFinished(ResultType result, Exception error) {
        progressDialog.dismiss();
        callback.onLongRunningActionFinished(result, error);
    }
}

/**
 * LICENSE STATEMENT (DO NOT REMOVE):
 * This code is in the public domain. You may use, alter, and redistribute it
 * free of any charges or obligations, with the following exceptions:
 * 1. You are not allowed to remove the statement naming the original author.
 * 2. You are not allowed to remove this license statement.
 *
 * @author Matthias Kaeppler
 */
public interface LongRunningActionCallback<ResultType> {

    /**
     * Called when the callable provided to
     * {@link LongRunningActionDispatcher.startLongRunningAction} completes.
     *
     * @param <ResultType>
     *            The result type of callable.call()
     * @param result
     *            Whatever the callable returns if it completes successfully, or
     *            null if an exception was thrown
     * @param error
     *            Whatever the callable throws if it executes in error, or null
     *            if it completed successfully
     */
    void onLongRunningActionFinished(ResultType result, Exception error);
}

Sometimes it is necessary to reach beyond the dynamics of Grails’ finder methods and other injected features, and instead make use of the underlying objects that implement this behavior directly. An example would be the need to access a Hibernate session directly, maybe because  you are writing a service object in plain Java, which means none of the features that rely on the dynamic nature of Groovy are available to you. These include:

  • the current Hibernate session
  • the Spring message source for accessing text in various translations
  • a logger object

All these objects are injected by Grails into your services automatically — that is, if you follow the conventions and are programming in Groovy. If you’re not, you’re pretty much on your own, because the Grails reference guide does not make any mention of these objects and how to access them.

However, I figured they’d be simply Spring beans as well (with the exception of log objects, see below), and due to the consistent conventions in Grails I soon discovered that they are called sessionFactory and messageSource respectively. So in order to access them directly from within a Groovy service object, just do this:


class MyService {
   def sessionFactory
   def messageSource

   def serviceMethod = {
      def session = sessionFactory.getCurrentSession()
      ...
   }
}

That doesn’t work with Java services of course. In that case, go to resources.groovy and add a bean declaration:


 myService(com.example.services.MyService) {
    sessionFactory = ref("sessionFactory")
    messageSource = ref("messageSource")
 }

Note that log objects have to be created manually, because they are not handled by the Spring container. You could for instance create a Log4J object, as shown below. Do not forget to add the setters for the Spring beans, so Grails can set them for you automatically:


import org.apache.log4j.Logger;
import org.hibernate.SessionFactory;
import org.springframework.context.MessageSource;

public class MyService {
    // instantiates a Log4J logger object
    private Logger log = Logger.getLogger(getClass().getName());

    // a Hibernate SessionFactory
    private SessionFactory sessionFactory;

    // a Spring MessageSource
    private MessageSource messageSource;

    [...]

    // used by the Spring IoC container to inject the session factory bean
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    // used by the Spring IoC container to inject the message source bean
    public void setMessageSource(MessageSource messageSource) {
        this.messageSource = messageSource;
    }
}

It may be a good idea to use a class like this as a base class for all your plain Java services, so they can benefit from these objects, too.


I guess you probably all know the wonderful Web 2.0 buzzword cloud by Markus Angermeier.

I found it was too dark in color when printing it on paper, so I did a re-coloring. I simply inverted the colors and adjusted the color curves with GIMP. Here’s the result:

Web 2.0 Map - recolored

Web 2.0 Map - recolored

I felt it’d be a lot easier on the eyes… and does not stress your printer’s toner as much. Please name the original author, Markus Angermeier, when using this graphic.


I have to admit I am currently somewhat hyped about the mobile Web, particularly in face of its opportunities for mobile social media. A recent survey by Nielsen Mobile shows that especially in the U.S. the adoption of the mobile Web experiences rapid growth:

We believe mobile Internet has reached a critical mass as an advertising medium in the US. As of May 2008, there were 40 million active users of the mobile Internet in the US, with individual sites that attract millions of unique users.

Source: Nielsen Mobile

Since my thesis focuses on mobile social media, I can hardly wait to see similar growth (and price drops) here in Germany — fixed price subscription plans are still too expensive.


I’m fairly disappointed with Google’s Android SDK.

Despite its alpha state, one could expect a little more given that people are bound to the SDK for successfully developing applications for the Google Android Challenge. The basic infrastructure is there, but many important features of the emulator that are required to test your app under realistic conditions have not been implemented yet. There are camera hooks; but no camera trigger (or even an image capturing stub). You can simulate network latency, but you cannot control network disconnection. Worst of all: No custom location providers are supported (the reference manual claims they are, but Android will refuse to load any location provider using a custom class via the LocationManager), which hinders development of location-based services a lot (see fellow developers struggle with this problem).

UPDATE: I figured out that it’s a problem with the ClassLoader. The emulator correctly detects the required class text-file in /data/misc/location/your_provider which contains the name of your provider class, but the class loader that’s active by then cannot see your class and thus fails to load it, effectively rendering this whole thing useless.

The mock provider provided by Google is unflexible to say the least; it has no communication interface to change the location on-the-fly. To set a new route one has to modify a file and restart the emulator, which seems not feasible given that some people reported emulator load times of up to several minutes.

The API also feels awkward at times, especially the widget API. Not much thought seems to have been put into it. For example, Button derives from TextView, which has the odd side-effect that you can declare e.g. a RadioButton to contain a “Phone Number” or “Password”. One can also find methods in derived classes that make no sense in that context; they usually throw exceptions telling you to not use this method when being invoked.

Weaknesses can also be observed in many other parts of the API. Ever tried to spawn a button-less dialog? Dig this:

showAlert("Title", 0, "The message", null, null, null,
        null, null, null, false, null);

Pretty eh? Things like this can be found in more than one place.

I won’t even go into emulator performance — it’s poor, to say the least.

The lifecycle management — while a little confusing at first — is decent however. Once you dug yourself through the many states an Activity can be in, you will find that Activity and Service lifecycle has been well designed and allows for much flexibility in developing your application logic.

So far my first impressions.


More Automated Builds

here is a little hint on how to make building your Grails apps from within Eclipse a little less explicit. The goal is to avoid being asked to manually issue a “grails package” after each re-build. Plus, if you followed my hints on how to let Grails and Eclipse compile to the same directory structure, you won’t need to issue “grails clean” anymore. Simply running Project –> Clean… and then running the Grails launcher will suffice for successful compilation and deployment if you follow these instructions.

  1. Go to you project properties and select the Builders entry. You should see two builders: Groovy builder and Java builder.
  2. If you haven’t yet configured an external tools launcher for a “grails package”, click New…, otherwise import that builder
  3. If you have to create the builder, set the following fields:
    • set Location to the folder where the Grails executables reside on your system
    • set Working Directory to ${workspace_loc:/your_project_name}
    • set Arguments to “package”
    • In the Build Options tab, check the fields After a “Clean” and During manual builds
  4. move the new builder to the bottom

I would also advice to create a keyboard shortcut to issue a manual build. For myself, I reassigned Ctrl+B to “Build project” instead of “Build all”.

When you now “Clean” your project and run the “run-app” launcher (I assume you have one!), Eclipse will first build your project and package it for deployment. The packaging is conditional though: if the sources are up-to-date, the new builder will be excluded when launching the app.


Grails is a Web application framework for rapid prototyping based on the Groovy scripting language, a Java dialect that compiles to Java byte code. Grails strongest advantage over similar frameworks (e.g. AppFuse, to name a more popular one) is its consequent use of the Convention Over Configuration paradigm, making it almost a no-brainer to set up a full featured Web app using Grails. Grails also relies heavily on the dynamic nature of Groovy to provide useful utility functionality or inject commonly used features such as logging objects and CRUD accessors for domain objects at runtime.

While Grails is an excellent piece of software, it is completely beyond me why apparently almost zero effort has been undergone to make it work flawlessly with one of the most (if not THE) widely used IDEs out there: the Eclipse IDE.

From day one, I had nothing but trouble running Grails applications in Eclipse. There is a document over at grails.org which has instructions for Grails + Eclipse, but it is erroneous and contradictory. While I still suggest to read that document, I will offer solutions to most of the problems you will probably still encounter, even after following that guide.

All instructions below worked for Grails 1.0.2 and Eclipse 3.3 on Ubuntu Linux 8.04.

The Groovy Eclipse Plugin

I assume that the Eclipse Groovy plugin has been installed.

By default, the output directory setting for Groovy compilations is left empty. That means your Groovy files in Eclipse will be compiled to your Grails project root. That is bad, as it confuses Grails. Set it to a different folder, e.g. bin-groovy. In any case, regardless what the Eclipse guide over at grails.org says, leave the “Disable Groovy Compiler Generating Class Files” box unchecked, but check “Disable Check Package Matches Source Directory”. Two reasons: If you check the first one, you won’t be able to debug your Groovy sources, but of course you want that. If you leave the second unchecked, you will get errors related to Groovy’s Spring DSL config in resources.groovy. This error is safe to ignore however.

Make sure to follow the instructions for debugging Groovy applications in Eclipse, too.

Fixing The Class-File Hell: 1st Try

Grails will generate an Eclipse .project file for you after issueing ‘grails create-app’. However, there are several issues with that file. First of all, Grails always compiles your Groovy and Java sources to the directory ~/.grails/<ver>/projects/<proj>/classes, but the generated project file sets the default output folder for Java class files to web-app/WEB-INF/classes. Furthermore, the Groovy plugin may compile to yet another directory, say bin-groovy. In other words, by default an imported Grails app is doomed to make you trouble, because the ‘grails’ command will always compile to a different folder than Eclipse does, and you will end up in a class-file mess where Eclipse will not recognize any changes made by Grails and vice versa.

The solution obviously is to make both Grails, the Eclipse Java compiler and the Groovy Eclipse plugin all compile to the same output directory. However, this is not as easy as it sounds. The best solution would be to tell Grails to compile to a different directory, but I haven’t found a way to do so.

The next best solution would be to create two symbolic links in your project root: bin and bin-groovy, both pointing to the Grails output directory (see above, it’s in your home directory under .grails/) and then tell the Eclipse Java compiler to compile to bin/ (via Project -> Properties -> Java Build Path -> Default Output Folder) and the Groovy plugin to compile to bin-groovy (via Project -> Properties -> Groovy Project Properties -> Groovy compiler output location).

While this solves the class-file hell, it leads to a weird problem: At least for me, I wasn’t able to run my Grails app from its Eclipse launcher anymore without the launch failing with a cryptic error message:

Failed to compile configuration file <proj_path>/grails-app/conf/Config.groovy: No signature of method: groovy.util.ConfigSlurper.parse() is applicable for argument types: (Config)

As of now, no one on the grails-user mailing list was able to say what is causing this, but of course it’s a show stopper, because you won’t be able to debug your application anymore (please note that this only affects Eclipse, your app would still start from the command line using ‘grails run-app’).

The third solution is somewhat ugly, but it works.

Fixing The Class-File Hell: 2nd Try

This approach is as follows: Instead of symlinking from bin and bin-groovy to the Grails output folder, we do it vice versa:

  1. leave the Java output folder to its default value: web-app/WEB-INF/classes
  2. the Groovy class files must also go there, so set a bin-groovy symlink to the same directory
  3. add bin-groovy to the build path: Project -> Properties -> Java Build Path -> Libraries -> Add Class Folder
  4. go to ~/.grails/<ver>/projects/<proj>, remove the classes/ folder and create a symlink “classes” to the full path of the web-app/WEB-INF/classes directory (e.g. ~/workspace/<proj>/web-app/WEB-INF/classes)

What we have done now is to turn around the sym-linkage: Grails will now compile to web-app/WEB-INF/classes. You may ask: What is the difference to the first approach?! Actually, there shouldn’t be any difference, but now you will notice that the ConfigSlurper error is gone, and you can run your app again from within Eclipse. THAT is why we do it (in lack of a better explanation…).

Unfortunately we’re still not 100% done. The reason is: As soon as you issue a ‘grails clean’, the symlink will become invalid and any “creational” follow-up commands like ‘grails compile’ will fail. The problem here is that ‘grails clean’ does not merely empty the ‘classes’ directory, but removes it alltogether. So we need to tell Grails not to remove the directory upon clean, but only its contents. A little background: Whenever you issue a command like this, you are effectively executing a Gant script (that’s the Groovy DSL for Apache Ant). These scripts reside in your Grails installation directory, in the scripts/ subfolder. Find the Clean.groovy script and perform the following changes:

  1. Find the target cleanCompiledSources
  2. Change the first call to Ant.delete to this code:
    Ant.delete(includeEmptyDirs:true) {
       fileset(dir:"${webInf}/classes", includes:"**/*")
    }
  3. Remove/comment the line Ant.delete(dir:classesDirPath)

save the file and exit. When you now run ‘grails clean’, only the content of classes/ will be removed, not the directory itself.

And that’s it!

Grails and Eclipse should now love each other. Ain’t that a happy ending?


The Bleeding Edge

I have to admit, when it comes to software, I am always grinding the bleeding edge. So what can I say, of course I was eager to take the new Long Term Support release of the wonderful Ubuntu Linux distribution for a test ride (in fact, I installed the RC on my laptop, which is my production machine… risky, but the idea of having 3D desktop effects out of the box was just too tempting).

The Grumpy Gibbon

First things first though. I have to say that I was quite disappointed with the last Ubuntu release — Gutsy Gibbon. I installed it on my desktop machine 6 months ago and I had nothing but trouble. Took me almost two weeks to fix the system so I could run my ordinary desktop tasks (don’t want to go into detail here, but how about not being able to boot into your newly installed system… makes you lose your faith rather quickly). In other words, I was rather anxious whether Hardy would follow Gutsy’s path of doom, or get back on the righteous path of a solid Dapper.

The Heron can fly — but does its feathering impress?

To break the tension: I am fairly satisfied.

I wrote a guide for Linux on Laptops and Tuxmobil once covering hardware compatibility (and how to fix it…) for Ubuntu Edgy and this very same notebook. For Hardy, I wouldn’t even know what to put into that guide. Every piece of hardware worked out of the box, from Wi-Fi to sound to 3D graphics. So, no complaints here. Installation was also a breeze, I still can’t understand why people claim that Linux would be much more difficult to install than Windows. Fedora, Ubuntu, SUSE, … they all manage to deliver a simple and straightforward installation procedure.

Now that I have run Hardy for over a week, I dug deep enough into the system to find some flaws though. I will list them below; for most of them I am not yet aware of a fix. I will post fix proposals (or links to them) as soon as I find them.

Stability problems

I have to say right out front that in this week, Hardy dared to lock up on me completely — but only once. But still, this is something that has never happened to me with any Ubuntu release before. I just don’t expect Linux to hardlock on me, it just… doesn’t feel right. Only Windows does that right?! Sarcasm aside, it was not the only stability problem:

Glipper

The Glipper applet (yes, Glipper is a panel applet now) keeps crashing on me after logging in. I am not aware of any fix yet.

Evince

The Evince PDF reader keeps getting stuck on “Loading…” a PDF much too often for my taste. Exiting and reopening it resolves the problem though, but still, this is annoying and demands fixage.

Firefox

Despite Hardy being an LTS release, it was shipped with a beta version of Firefox 3 (Beta 5). As expected, this brings with it all the stability issues of a beta release. From plain crashes to Firefox completely messing up sound and video (I was once not able to properly play back video files as long as FF would be running; only after exiting FF video playback went back to normal). Since Ubuntu will constantly update FF as it matures however, this won’t be much of a problem anymore later this year.

NetworkManager

I sometimes had problems to connect to my wireless network (it would just stay in connect phase endlessly without actually connecting). To fix it, I had to re-create the network. On shutdown, I also get errors related to NetworkManager (see Bug #216908). Doesn’t feel very polished.

Suspend/Hibernate

Sigh. I am still waiting for the day when the Linux kernel will actually support hibernation and suspend (I mean seriosuly support it, not just saying it would). Because, it is still as broken in Hardy as in any other Linux distribution I have ever used. In 50% of the time, your laptop will not recover from suspension in a healthy way (either your screen keeps blank, or the network is broken). I remain faithful though.

Glitches and other problems

I observed glitches with several software components, in particular in conjunction with compiz and 3D desktop effects.

Compiz

The compiz glitches are visible, but not serious. From messed up color palettes on inactive window title bars to flickering buttons on window decorations to completely disappearing widgets on Java based applications. The latter can be fixed though by setting an environment variable:

AWT_TOOLKIT=MToolkit

VLC

With the change to Pulse Audio in Hardy, VLC media player by default does not play back audio streams. Read this thread for a solution.

But still!

Apart from these (rather small) annoyances, Hardy is an excellent Ubuntu. Ubuntu more and more is simply Doing Things Right (TM). From the ease of detecting and installing required (non-free) media codecs, to the easy activation of 3D desktop effects (and detection and installation of proper proprietary drivers), it really makes progress in overall ease of use. Although FF3 is still a bit unstable, I am glad they included it. The performance improvements over FF2 are significant. The new printing interface looks more complicated at first, but simply works better than ever before. It keeps configuration required by the user at a minimum.

Bottom line being, I am pretty happy with Hardy Heron. It seems to be a very solid foundation for the upcoming Intrepid Ibex.

Enter the next round!


This may be old news to some, but although I had heard of using cURL for downloading files off the internet from a command line (as a wget alternative), I didn’t know that it was actually capable of doing much, much more. In fact, cURL is a very good allrounder at doing anything related to transmitting and receiving data using popular protocols such as HTTP(S), (S)FTP, TELNET, LDAP and more. In particular, you can use it to test your Web application with very little effort!

I am currently programming for a Java Servlet based Web application, where I need to assemble HTTP requests on the client side and dissect them on the server side. More precisely, I have a servlet which extracts data from an HTTP multipart request (using Apache Commons FileUpload) and hand it over to the logic for further processing. Of course, I have to write lots of code for checking whether requests are valid and so forth. So, in order to test this code, I want to send “malformed” requests, by which I mean requests that do not carry data expected by the application in its current state. Instead of setting up a complex test environment or even modifying your client code to send bad data, you can simply use cURL.

cURL uses a simple command line interface; I will show you the most important flags. Let’s assume we have a simple RESTful Web application which manages books. We could use cURL to retrieve the list of available books like this:

curl http://localhost:8080/mybookstore/books -v

This will issue an HTTP GET on the specified URL. The -v flag tells cURL to be verbose, which means it will print all sent and received HTTP headers, the payload of the server reply, plus some additional status information to the standard output. The complete output might look something like this:

* About to connect() to localhost port 8080 (#0)
* Trying 127.0.0.1… connected
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /mybookstore/books HTTP/1.1
> User-Agent: curl/7.18.0 (i586-pc-mingw32msvc) libcurl/7.18.0 zlib/1.2.3
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/xml;charset=utf-8
< …
< <books><book author=”David Flanagan”>Java In A Nutshell</book><book author=”Stephen Hawking”>A Brief History Of Time</book>…</books>

The > symbol indicates data going to the server, while < indicates data coming from the server. If that’s not verbose enough for you, try using the –trace-ascii <filename> argument instead. It will log the client/server conversation to the specified file.

So, what about sending data? It’s actually just as easy. Suppose we want to store a new book by supplying its title and author:

curl http://localhost:8080/mybookstore/books -F “title=Wikinomics” -F “author=D. Tapscott, A. D. Williams”

What this does is issueing an HTTP POST to the specified address, submitting the given data using the multipart/form-data MIME type. Now that was easy! If you don’t want to type all the input data on the command line, you can also let cURL read data from a file by prefixing the value with @ followed by the file name, which makes perfect sense when transmitting binary data or long text documents. As to the way how cURL transmits the data, you can also use the -d flag, which will result in the data being POST-ed as an application/x-www-form-urlencoded string, or use the same -d flag in combination with -G to issue a GET request instead and sending the data as a URL parameter (query string).

Another noteworthy aspect about cURL is that you can use it to manage cookies. Let’s assume our book server requires the user to login first, and that the server remembers us by setting a session cookie. Ponder the commands below, where we use cURL first to authenticate with our server, and then add a new book in the same session:

curl http://localhost:8080/mybookstore/login -d “username=john” -d “password=doe” -c cookies.txt
curl http://localhost:8080/mybookstore/books -b cookies.txt -F “title=…” …

In the first line, we tell cURL to use the file cookies.txt as its “cookie jar”, which means that it will store all cookies set by the server in this file. We can leverage the session data stored in that file (for a Servlet container this would be a JSESSIONID) in consecutive requests in order to remain authenticated, by setting the value of the -b flag to the cookie jar. You can also use key/value pairs as an argument to -b, but using an intermediary file is more convenient.

cURL is free software and can be downloaded for a broad range of platforms from the cURL website.


As posted recently on Cybernet News, a profile-guided optimization build of the upcoming version 3 of the Firefox Web browser has appeared which performs the SunSpider JavaScript Benchmark in only one third of the time required by Firefox 2.0.0.12.

This sounds great at first, but if you take a closer look, you see that this benchmark for example does not take DOM operations into account. AJAX heavy websites however basically evolve around modifying a page’s DOM tree at runtime, which can quickly become the major bottle neck when requesting and re-rendering large amounts of data.

Instead of focusing on DOM operations, the benchmark runs exotic tests like 3D raytracing and cryptographic calculations which do not seem to be typical use cases for Web 2.0 content.

For my part, I remain skeptical, although I would of course welcome performance improvements in any part of Firefox in upcoming releases.