Archive

Archive for January 12, 2011

Going Asynchronous using AsyncHttpClient: For Dummies

January 12, 2011 10 comments

Concluding on my “Going Asynchronous using AsyncHttpClient (Basic and Complex)” blog series, this week I will explains how to use the new SimpleAsynHttpClient API to write powerful async client applications.

The SimpleAsynchHttpClient is an implementation on top of the AsyncHttpClient and its builders class like AsyncHttpClientConfigBuilder, RealmBuilder etc. With SimpleAsynchHttpClient, all you need to do is to create a single builder, and configure your request from that builder:

 SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder()
                .setUrl("http://..")
                .setProxyHost("http://...")
                .addHeader("foo","bar")
                .setRealmPrincipal("me")
                .setRealmPassword("pwd")
                .build();

Once you have configured your SimpleAsyncHttpClient, you can invoke your HTTP method:

 client.get();
   or
 client.head();

Now the biggest difference with the AsyncHttpClient API is the support for BodyConsumer and BodyGenerator. The BodyGenerator API allows an application to customize how bytes are sent. This is quite useful when you need to stream large fie and you don’t want to load it in memory. As an example, the library contains a FileBodyGenerator defined as

public class FileBodyGenerator implements BodyGenerator {

    private final File file;

    public FileBodyGenerator(File file) {
        this.file = file;
    }

    public RandomAccessBody createBody() throws IOException {
        return new FileBody(file);
    }

    protected static class FileBody
            implements RandomAccessBody {

        private final RandomAccessFile file;

        private final FileChannel channel;

        private final long length;

        public FileBody(File file)
                throws IOException {
            this.file = new RandomAccessFile(file, "r");
            channel = this.file.getChannel();
            length = this.file.length();
        }

        public long getContentLength() {
            return length;
        }

        public long read(ByteBuffer buffer)
                throws IOException {
            return channel.read(buffer);
        }

        public long transferTo(long position,
                               long count, WritableByteChannel target)
                throws IOException {
            return channel.transferTo(position, count, target);
        }

        public void close()
                throws IOException {
            file.close();
        }
    }
}

The library ships with InpuStreamBodyGenerator, ByteArrayBodyGenerator and the one above. Those BodyGenerator never buffers bytes in memory, reducing the probability to fill the heap and get an OOM. For the response, the SimpleAsyncHttpClient support a new API called BodyConsumer, which is simply defined as

public interface BodyConsumer {

    void consume(ByteBuffer byteBuffer) throws IOException;

    void close() throws IOException;

}

Simple callback that are invoked as soon as the response’s bytes are available. As an example, the library ships with

public class AppendableBodyConsumer implements BodyConsumer {

    private final Appendable appendable;
    private final String encoding;

    public AppendableBodyConsumer(Appendable appendable, String encoding) {
        this.appendable = appendable;
        this.encoding = encoding;
    }

    public AppendableBodyConsumer(Appendable appendable) {
        this.appendable = appendable;
        this.encoding = "UTF-8";
    }

    public void consume(ByteBuffer byteBuffer) throws IOException {
        appendable.append(new String(byteBuffer.array(), encoding));
    }

    public void close() throws IOException {
        if (Closeable.class.isAssignableFrom(appendable.getClass())) {
            Closeable.class.cast(appendable).close();
        }
    }
}

The library ships with ByteBufferBodyConsumer, FileBodyConsumer and OutputStreamBodyConsumer. Hence normally you shouldn’t have to write BodyConsumer and BodyGenerator…just use the one available! Simply do:

 SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder()
                .setIdleConnectionInPoolTimeoutInMs(100)
                .setUrl("http://.....)
                .setHeader("Content-Type", "text/html")
                .build();

  Future future = client.post(
                new InputStreamBodyGenerator(myInputStream),
                new OutputStreamBodyConsumer(myOutputStream));

  Response response = future.get();

Finally, if you need to re-use your instance of SimpleAsyncHttpClient, you an use the Request API directly like when you work with AsyncHttpClient:

 SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder()
                .setIdleConnectionInPoolTimeoutInMs(100)
                .setUrl("http://.....)
                .setHeader("Content-Type", "text/html")
                .build();

  Future future = client.post(
                new InputStreamBodyGenerator(myInputStream),
                new OutputStreamBodyConsumer(myOutputStream));

  Response response = future.get();

  client.get(new RequestBuilder().setUrl("http://...").build(),
             new OutputStreamBodyConsumer(myOutputStream));
  response = future.get();  

The SimpleAsyncHttpClient will be included in 1.5.0, which is targeted to ship weeks of January 18. For any questions you can use our Google Group, irc.freenode.net #asynchttpclient or use Twitter to reach me! You can checkout the code on Github, download the jars from Maven Central or use Maven:

<dependency>
    <groupId>com.ning</groupId>
    <artifactId>async-http-client</artifactId>
    <version>1.5.0-SNAPSHOT</version>
</dependency>

Categories: Async Http client
Follow

Get every new post delivered to your Inbox.

Join 51 other followers