Dropwizard Client Config for Jersey/HTTP Client

In the previous tutorial, we learned to create REST APIs using DropWizard. Now let’s build a REST client for consuming REST APIs across the network. Dropwizard includes both Apache HttpClient and Jersey Client.

1. Maven Dependency

The dropwizard client module is added as a separate module.

<dependency>
    <groupId>io.dropwizard</groupId>
    <artifactId>dropwizard-client</artifactId>
    <version>4.0.0</version>
</dependency>

2. Apache HTTPClient Configuration

The underlying library for dropwizard-client is Apache’s HttpClient.

2.1. Configuration

Dropwizard provides easy-to-declare and use REST client configuration. We need to create HttpClientConfiguration and give it io.dropwizard.setup.Environment reference.

import com.fasterxml.jackson.annotation.JsonProperty;
import io.dropwizard.client.HttpClientConfiguration;
import io.dropwizard.core.Configuration;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;

public class ApplicationConfiguration extends Configuration {

  @Valid
  @NotNull
  private HttpClientConfiguration httpClient = new HttpClientConfiguration();

  @JsonProperty("httpClient")
  public HttpClientConfiguration getHttpClientConfiguration() {
    return httpClient;
  }

  @JsonProperty("httpClient")
  public void setHttpClientConfiguration(HttpClientConfiguration httpClient) {
    this.httpClient = httpClient;
  }
}

To register the HTTP Client, use the e.jersey().register(new APIController(httpClient)) method with the controller that needs access to HttpClient.

public class App extends Application<ApplicationConfiguration> {

  @Override
  public void initialize(Bootstrap<ApplicationConfiguration> b) {
  }

  @Override
  public void run(ApplicationConfiguration c, Environment e) throws Exception {

    LOGGER.info("Registering REST resources");
    e.jersey().register(new EmployeeController(e.getValidator(), new EmployeeRepository()));

    LOGGER.info("Registering Application Health Check");
    e.healthChecks().register("application", new ApplicationHealthCheck());

    LOGGER.info("Registering Apache HttpClient");
    final HttpClient httpClient = new HttpClientBuilder(e)
        .using(c.getHttpClientConfiguration())
        .build(getName());
    e.jersey().register(new APIController(httpClient));
  }

  public static void main(String[] args) throws Exception {
    new App().run(args);
  }
}

Finally, we have injected the HTTP client in APIController as follows:

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.core.Response;
import org.apache.hc.client5.http.classic.HttpClient;

@Path("client-root-path")
public class APIController {

  private HttpClient httpClient;

  public APIController(HttpClient httpClient) {
    this.httpClient = httpClient;
  }

  @GET
  @Path("/{id}")
  public Response getEmployeeById(@PathParam("id") Integer id) {
    //use httpClient to access the remote resource
    return null;
  }
}

2.2. Default Properties

The default configuration for HttpClientConfiguration is as follows:

  • timeout: 500ms
  • connectionTimeout: 500ms
  • timeToLive: 1 hour
  • cookiesEnabled: false
  • maxConnections: 1024
  • maxConnectionsPerRoute: 1024
  • keepAlive: 0s

2.3. Custom Configuration for HttpClient

Add the custom configuration into a YAML file as follows:

httpClient:
  timeout: 100ms
  connectionTimeout: 1000ms
  timeToLive: 1h
  cookiesEnabled: true
  maxConnections: 1024
  maxConnectionsPerRoute: 1024
  keepAlive: 0ms
  retries: 0

Now we can inject the configuration while running the application as a command line parameter to the server command:

java -jar target/hello-world-0.0.1-SNAPSHOT.jar server app-config.yml

3. Jersey Client Configuration

3.1. Configuration

If you wish to use Jersey client, instead of HttpClient, for easy-to-use APIs and simple serialization and deserialization methods for POJOs, we can inject JerseyClient as follows:

public class ApplicationConfiguration extends Configuration {

  @Valid
  @NotNull
  private JerseyClientConfiguration jerseyClient = new JerseyClientConfiguration();

  @JsonProperty("jerseyClient")
  public JerseyClientConfiguration getJerseyClientConfiguration() {
    return jerseyClient;
  }

  @JsonProperty("jerseyClient")
  public void setJerseyClientConfiguration(JerseyClientConfiguration jerseyClient) {
    this.jerseyClient = jerseyClient;
  }
}

Now we can inject the JerseyClient similar to the previous approach discussed above,

final Client jerseyClient = new JerseyClientBuilder(e)
  .using(c.getJerseyClientConfiguration())
  .build(getName());

e.jersey().register(new APIController(jerseyClient));

Now we can use the JerseyClient in the controller as follows:

@Path("client-root-path")
public class APIController {

  private JerseyClient client;

  public APIController(JerseyClient client) {
    this.client = client;
  }

  @GET
  @Path("/{id}")
  public Response getEmployeeById(@PathParam("id") Integer id) {
    //use client to access the remote resource
    return null;
  }
}

3.2. Custom Properties

The default configuration for JerseyClientConfiguration is as follows. You can change their values accordingly.

jerseyClient:
  minThreads: 1
  maxThreads: 128
  workQueueSize: 8
  gzipEnabled: true
  gzipEnabledForRequests: true
  chunkedEncodingEnabled: true

4. Demo

Now when you have access to jakarta.ws.rs.client.Client or org.apache.http.client.HttpClient inside REST client resource APIController.java, you can library specific methods to call HTTP URIs as usual.

@Path("client-root-path")
public class APIController {

  private JerseyClient jerseyClient;

  public APIController(JerseyClient jerseyClient) {
    this.jerseyClient = jerseyClient;
  }

  @GET
  @Path("/employees/")
  public String getEmployees() {

    //Do not hard code the path in your application
    WebTarget webTarget = jerseyClient.target("http://localhost:8080/employees");

    Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_JSON);

    Response response = invocationBuilder.get();
    ArrayList<Employee> employees = response.readEntity(ArrayList.class);
    return employees.toString();
  }

  @GET
  @Path("/employees/{id}")
  public String getEmployeeById(@PathParam("id") int id) {

    //Do not hard code the path in your application
    WebTarget webTarget = jerseyClient.target("http://localhost:8080/employees/" + id);

    Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_JSON);

    Response response = invocationBuilder.get();
    Employee employee = response.readEntity(Employee.class);
    return employee.toString();
  }
}

In the above class, we accessed REST APIs created in dropwizard hello world tutorial.

After accessing the APIs, I returned the response in plain text form as shown in the below image.

Drop Wizard REST Client
Drop Wizard REST Client

I have set the context path of the client resource class to /client/ to logically separate the URIs of client and service endpoints.

Drop me your questions in the comments section.

Happy Learning !!

Sourcecode on Github

Comments are closed for this article!

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.