1. Overview
In this article, we’ll have a look at the Google HTTP Client Library for Java, which is a fast, well-abstracted library for accessing any resources via the HTTP connection protocol.
The main features of the client are:
- an HTTP abstraction layer that lets you decouple any low-level library
- fast, efficient and flexible JSON and XML parsing models of the HTTP response and request content
- easy to use annotations and abstractions for HTTP resource mappings
The library can also be used in Java 5 and above, making it a considerable choice for legacy (SE and EE ) projects.
In this article, we’re going to develop a simple application that will connect to the GitHub API and retrieve users, while covering some of the most interesting features of the library.
2. Maven Dependencies
To use the library we’ll need the google-http-client dependency:
<dependency> <groupId>com.google.http-client</groupId> <artifactId>google-http-client</artifactId> <version>1.23.0</version> </dependency>
The latest version can be found at Maven Central.
3. Making a Simple Request
Let’s start by making a simple GET request to the GitHub page to showcase how the Google Http Client works out of the box:
HttpRequestFactory requestFactory = new NetHttpTransport().createRequestFactory(); HttpRequest request = requestFactory.buildGetRequest( new GenericUrl("https://github.com")); String rawResponse = request.execute().parseAsString()
To make the simplest of request we’ll need at least:
- HttpRequestFactory this is used to build our requests
- HttpTransport an abstraction of the low-level HTTP transport layer
- GenericUrl a class that wraps the Url
- HttpRequest handles the actual execution of the request
We’ll go through all these and a more complex example with an actual API that returns a JSON format in the following sections.
4. Pluggable HTTP Transport
The library has a well-abstracted HttpTransport class that allows us to build on top of it and change to the underlying low-level HTTP transport library of choice:
public class GitHubExample { static HttpTransport HTTP_TRANSPORT = new NetHttpTransport(); }
In this example, we’re using the NetHttpTransport, which is based on the HttpURLConnection that is found in all Java SDKs. This is a good starting choice since it’s well-known and reliable.
Of course, there might be the case where we need some advanced customization, and thus the requirement of a more complex low-level library.
For this kind of cases, there is the ApacheHttpTransport:
public class GitHubExample { static HttpTransport HTTP_TRANSPORT = new ApacheHttpTransport(); }
The ApacheHttpTransport is based on the popular Apache HttpClient which includes a wide variety of choices to configure connections.
Additionally, the library provides the option to build your low-level implementation, making it very flexible.
5. JSON Parsing
The Google Http Client includes another abstraction for JSON parsing. A major advantage of this is that the choice of low-level parsing library is interchangeable.
There’re three built-in choices, all of which extend JsonFactory, and it also includes the possibility of implementing our own.
5.1. Interchangeable Parsing library
In our example, we’re going to use the Jackson2 implementation, which requires the google-http-client-jackson2 dependency:
<dependency> <groupId>com.google.http-client</groupId> <artifactId>google-http-client-jackson2</artifactId> <version>1.23.0</version> </dependency>
Following this, we can now include the JsonFactory:
public class GitHubExample { static HttpTransport HTTP_TRANSPORT = new NetHttpTransport(); staticJsonFactory JSON_FACTORY = new JacksonFactory(); }
The JacksonFactory is the fastest and most popular library for parsing/serialization operations.
This comes at the cost of the library size (which could be a concern in certain situations). For this reason, Google also provides the GsonFactory, which is an implementation of the Google GSON library, a light-weight JSON parsing library.
There is also the possibility of writing our low-level parser implementation.
5.2. The @Key annotation
We can use the @Key annotation to indicate fields that need to be parsed from or serialized to JSON:
public class User { @Key private String login; @Key private long id; @Key("email") private String email; // standard getters and setters }
Here we’re making a User abstraction, which we receive in batch from the GitHub API (we will get to the actual parsing later in this article).
Please note that fields that don’t have the @Key annotation are considered internal and are not parsed from or serialized to JSON. Also, the visibility of the fields does not matter, nor does the existence of the getter or setter methods.
We can specify the value of the @Key annotation, to map it to the correct JSON key.
5.3. GenericJson
Only the fields we declare, and mark as @Key are parsed.
To retain the other content, we can declare our class to extend GenericJson:
public class User extends GenericJson { //... }
GenericJson implements the Map interface, which means we can use the get and put methods to set/get JSON content in the request/response.
6. Making the Call
To connect to an endpoint with the Google Http Client, we’ll need an HttpRequestFactory, which will be configured with our previous abstractions HttpTransport and JsonFactory:
public class GitHubExample { static HttpTransport HTTP_TRANSPORT = new NetHttpTransport(); static JsonFactory JSON_FACTORY = new JacksonFactory(); private static void run() throws Exception { HttpRequestFactory requestFactory = HTTP_TRANSPORT.createRequestFactory( (HttpRequest request) -> { request.setParser(new JsonObjectParser(JSON_FACTORY)); }); } }
The next thing we’re going to need is a URL to connect to. The library handles this as a class extending GenericUrl on which any field declared is treated as a query parameter:
public class GitHubUrl extends GenericUrl { public GitHubUrl(String encodedUrl) { super(encodedUrl); } @Key public int per_page; }
Here in our GitHubUrl, we declare the per_page property to indicate how many users we want in a single call to the GitHub API.
Let’s continue building our call using the GitHubUrl:
private static void run() throws Exception { HttpRequestFactory requestFactory = HTTP_TRANSPORT.createRequestFactory( (HttpRequest request) -> { request.setParser(new JsonObjectParser(JSON_FACTORY)); }); GitHubUrl url = new GitHubUrl("https://api.github.com/users"); url.per_page = 10; HttpRequest request = requestFactory.buildGetRequest(url); Type type = new TypeToken<List<User>>() {}.getType(); List<User> users = (List<User>)request .execute() .parseAs(type); }
Notice how we specify how many users we’ll need for the API call, and then we build the request with the HttpRequestFactory.
Following this, since the GitHub API’s response contains a list of users, we need to provide a complex Type, which is a List<User>.
Then, on the last line, we make the call and parse the response to a list of our User class.
7. Custom Headers
One thing we usually do when making an API request is to include some kind of custom header or even a modified one:
HttpHeaders headers = request.getHeaders(); headers.setUserAgent("Baeldung Client"); headers.set("Time-Zone", "Europe/Amsterdam");
We do this by getting the HttpHeaders after we’ve created our request but before executing it and adding the necessary values.
Please be aware that the Google Http Client includes some headers as special methods. The User-Agent header for example, if we try to include it with just the set method it would throw an error.
8. Exponential Backoff
Another important feature of the Google Http Client is the possibility to retry requests based on certain status codes and thresholds.
We can include our exponential backoff settings right after we’ve created our request object:
ExponentialBackOff backoff = new ExponentialBackOff.Builder() .setInitialIntervalMillis(500) .setMaxElapsedTimeMillis(900000) .setMaxIntervalMillis(6000) .setMultiplier(1.5) .setRandomizationFactor(0.5) .build(); request.setUnsuccessfulResponseHandler( new HttpBackOffUnsuccessfulResponseHandler(backoff));
Exponential Backoff is turned off by default in HttpRequest, so we must include an instance of HttpUnsuccessfulResponseHandler to the HttpRequest to activate it.
9. Logging
The Google Http Client uses java.util.logging.Logger for logging HTTP request and response details, including URL, headers, and content.
Commonly, logging is managed using a logging.properties file:
handlers = java.util.logging.ConsoleHandler java.util.logging.ConsoleHandler.level = ALL com.google.api.client.http.level = ALL
In our example we use ConsoleHandler, but it’s also possible to choose the FileHandler.
The properties file configures the operation of the JDK logging facility. This config file can be specified as a system property:
-Djava.util.logging.config.file=logging.properties
So after setting the file and system property, the library will produce a log like the following:
-------------- REQUEST -------------- GET https://api.github.com/users?page=1&per_page=10 Accept-Encoding: gzip User-Agent: Google-HTTP-Java-Client/1.23.0 (gzip) Nov 12, 2017 6:43:15 PM com.google.api.client.http.HttpRequest execute curl -v --compressed -H 'Accept-Encoding: gzip' -H 'User-Agent: Google-HTTP-Java-Client/1.23.0 (gzip)' -- 'https://api.github.com/users?page=1&per_page=10' Nov 12, 2017 6:43:16 PM com.google.api.client.http.HttpResponse -------------- RESPONSE -------------- HTTP/1.1 200 OK Status: 200 OK Transfer-Encoding: chunked Server: GitHub.com Access-Control-Allow-Origin: * ... Link: <https://api.github.com/users?page=1&per_page=10&since=19>; rel="next", <https://api.github.com/users{?since}>; rel="first" X-GitHub-Request-Id: 8D6A:1B54F:3377D97:3E37B36:5A08DC93 Content-Type: application/json; charset=utf-8 ...
10. Conclusion
In this tutorial, we’ve shown the Google HTTP Client Library for Java and its more useful features. Their Github contains more information about it as well as the source code of the library.
As always, the full source code of this tutorial is available over on GitHub.