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

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments

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.