View on GitHub

DavidWebb

Lightweight Java HTTP-Client for calling JSON REST-Services (especially for Android)

Download this project as a .zip file Download this project as a tar.gz file

DavidWebb

Lightweight Java HTTP-Client for calling JSON REST-Services (especially made for Android).

Problem

If you have to call a RESTful Webservice from Java, especially if you are on Android, you have some options:

Solved

DavidWebb is a small wrapper around HttpURLConnection. It supports most HTTP communication cases when you talk to REST services and your data is JSON. It is very lightweight (~18 KB jar) and super-easy to use.

Features

Non-Features

Following features are not supported and there are no plans to realize them:

Where not stated, the workaround is to use another library or implement it above of DavidWebb. If you think your implementation might be useful for others and it's not blowing up the size of the JAR, please create a pull request. (Adding heavy dependencies is not an option.)

Usage Examples

Below you can see some examples of how to use DavidWebb. And here you can find the API Documentation.

This is some code from a SyncAdapter of an Android App:

// create the client (one-time, can be used from different threads)
Webb webb = Webb.create();
webb.setBaseUri(SyncPreferences.REST_ENDPOINT);
webb.setDefaultHeader(Webb.HDR_USER_AGENT, Const.UA);

// later we authenticate
Response<JSONObject> response = webb
        .post("/session")
        .param("authentication", createAuthentication(syncPreferences))
        .param("deviceId", syncPrefs.getDeviceId())
        .ensureSuccess()
        .asJsonObject();

JSONObject apiResult = response.getBody();

AccessToken accessToken = new AccessToken();
accessToken.token = apiResult.getString("token");
accessToken.validUntil = apiResult.getLong("validUntil");

webb.setDefaultHeader(HDR_ACCESS_TOKEN, accessToken.token);

JSONObject sync = webb.post("/startSync")
        .param("lastSync", syncPrefs.getLastSync())
        .ensureSuccess()
        .asJsonObject()
        .getBody();

// ... etc. etc.

// releaseAccessToken
webb.delete("/session").asVoid();
accessToken = null;

Using Google Directions API:

Webb webb = Webb.create();
JSONObject result = webb
        .get("http://maps.googleapis.com/maps/api/directions/json")
        .param("origin", new GeoPoint(47.8227, 12.096933))
        .param("destination", new GeoPoint(47.8633, 12.215533))
        .param("mode", "walking")
        .param("sensor", "true")
        .ensureSuccess()
        .asJsonObject()
        .getBody();

JSONArray routes = result.getJSONArray("routes");

Deal with "connection reset by peer" or other recoverable errors

Android (at least >= GINGERBREAD) automatically sets the "Connection" header to "keep-alive". This sometimes causes errors, because the server might already have closed the connection without the mobile device knowing it. It would be cumbersome to cope with this and other situation where a retry solved all problems. Since version 1.2.0 it is more comfortable for you:

Webb webb = Webb.create();
JSONObject result = webb
        .get("https://example.com/api/request")
        .retry(1, false) // at most one retry, don't do exponential backoff
        .asJsonObject()
        .getBody();

In many cases you will need to change the behaviour of how and when to retry a request. For this, you can register your own RetryManager, see webb.setRetryManager().

Hint: Currently 1.2.0 is not yet released. Build from sources or download 1.2.0-SNAPSHOT from staging-server (see Maven Coordinates).

You have to do Basic Authentication?

This authorization method uses a Base64 encoded string. Unfortunately Java SE doesn't provide a Base64 encoder. Because DavidWebb wants to be light and Android already provides a Base64 support class, it's left to you to insert a few lines of code. As you can see, it's not hard work. Use one of the methods to set a header and set Authorization header by yourself:

byte[] credentials = (username + ":" + password).getBytes("UTF-8");
String auth = "Basic " + Base64.encodeToString(credentials, 0);
Webb webb = Webb.create();
webb.setDefaultHeader(Webb.HDR_AUTHORIZATION, auth);

More Samples

If you want to see more examples, just have a look at the JUnit TestCase (src/test/java/...).

Special Case Android < Froyo

You should add this if you build for legacy Android devices:

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
    System.setProperty("http.keepAlive", "false");
}

In Case Of Problems

I've encountered some problems which could be solved by disabling keep-alive of HTTP connections. The solution above did not always work on recent versions of Android. To be sure to do without keep-alive, you can set an HTTP header like this:

// disable 'keep-alive' for all requests created by this instance of Webb
webb.setDefaultHeader("Connection", "close");

// disable only for this request
Request<String> request = webb.post("/some-resource").header("Connection", "close");

Maven Coordinates

<dependency>
    <groupId>com.goebl</groupId>
    <artifactId>david-webb</artifactId>
    <version>1.1.0</version>
</dependency>

Gradle

'com.goebl:david-webb:1.1.0'

Not using Maven/Gradle? - Then you can download the plain JAR from following links directly:

Background

Not for you?

If DavidWebb is too lightweight and you're missing features, you can have a look at:

The Name!?

David Webb is the real name of Jason Bourne. So JSON and Web, did you get it? OK, might be silly, but Bourne 1-3 are my favorite films and so at least I can remember the name.

From Wikipedia:

Jason Bourne is a fictional character and the protagonist of a series of novels by Robert Ludlum and subsequent film adaptations

License

MIT License, see LICENSE file

Testing

The Unit-Tests do not mock any network-libraries, but depend on a small Express-application running.

Run following commands before you start the JUnit-tests or skip the tests in Maven build with command line option -DskipTests

cd src/test/api-test-server
npm install
node .

Android Tests

Before running the Android tests, build with maven, deploy the Android app and be sure to set the timezone of your emulator to the same as your PC and synchronize date/time, otherwise some tests will fail:

# synchronize time of emulator (otherwise problems with SSL are likely)
adb -e shell date -s `date +"%Y%m%d.%H%M%S"`

# run the tests
adb shell am instrument -w \
  -e class com.goebl.david.tests.DavidWebbAndroidTests \
  com.goebl.david/android.test.InstrumentationTestRunner

Coverage

The test coverage is measured only with the JUnit-tests for JavaSE. As mocking is not easy to accomplish under Android/Dalvik, coverage is not measured for this runtime environment.

TODO

Features (planned)

Features (only ideas)

Create an issue if you want to have one of those ideas implemented.

Documentation

Testing