5

DropWizard uses Jersey under the hood for REST. I am trying to figure out how to write a client for the RESTful endpoints my DropWizard app will expose.

For the sake of this example, let's say my DropWizard app has a CarResource, which exposes a few simple RESTful endpoints for CRUDding cars:

@Path("/cars")
public class CarResource extends Resource {
    // CRUDs car instances to some database (DAO).
    public CardDao carDao = new CarDao();

    @POST
    public Car createCar(String make, String model, String rgbColor) {
        Car car = new Car(make, model, rgbColor);
        carDao.saveCar(car);

        return car;
    }

    @GET
    @Path("/make/{make}")
    public List<Car> getCarsByMake(String make) {
        List<Car> cars = carDao.getCarsByMake(make);
        return cars;
    }
}

So I would imagine that a structured API client would be something like a CarServiceClient:

// Packaged up in a JAR library. Can be used by any Java executable to hit the Car Service
// endpoints.
public class CarServiceClient {
    public HttpClient httpClient;

    public Car createCar(String make, String model, String rgbColor) {
        // Use 'httpClient' to make an HTTP POST to the /cars endpoint.
        // Needs to deserialize JSON returned from server into a `Car` instance.
        // But also needs to handle if the server threw a `WebApplicationException` or
        // returned a NULL.
    }

    public List<Car> getCarsByMake(String make) {
        // Use 'httpClient' to make an HTTP GET to the /cars/make/{make} endpoint.
        // Needs to deserialize JSON returned from server into a list of `Car` instances.
        // But also needs to handle if the server threw a `WebApplicationException` or
        // returned a NULL.
    }
}

But the only two official references to Drop Wizard clients I can find are totally contradictory to one another:

  • DropWizard recommended project structure - which claims I should put my client code in a car-client project under car.service.client package; but then...
  • DropWizard Client manual - which makes it seem like a "DropWizard Client" is meant for integrating my DropWizard app with other RESTful web services (thus acting as a middleman).

So I ask, what is the standard way of writing Java API clients for your DropWizard web services? Does DropWizard have a client-library I can utilize for this type of use case? Am I supposed to be implementing the client via some Jersey client API? Can someone add pseudo-code to my CarServiceClient so I can understand how this would work?

smeeb
  • 27,777
  • 57
  • 250
  • 447

4 Answers4

2

Here is a pattern you can use using the JAX-RS client.

To get the client:

javax.ws.rs.client.Client init(JerseyClientConfiguration config, Environment environment) {
    return new JerseyClientBuilder(environment).using(config).build("my-client");
}

You can then make calls the following way:

javax.ws.rs.core.Response post = client
        .target("http://...")
        .request(MediaType.APPLICATION_JSON)
        .header("key", value)
        .accept(MediaType.APPLICATION_JSON)
        .post(Entity.json(myObj));
0

Yes, what dropwizard-client provides is only to be used by the service itself, most likely to communicate other services. It doesn't provide anything for client applications directly.

It doesn't do much magic with HttpClients anyway. It simply configures the client according to the yml file, assigns the existing Jackson object mapper and validator to Jersey client, and I think reuses the thread pool of the application. You can check all that on https://github.com/dropwizard/dropwizard/blob/master/dropwizard-client/src/main/java/io/dropwizard/client/JerseyClientBuilder.java

I think I'd go about and structure my classes as you did using Jersey Client. Following is an abstract class I've been using for client services:

public abstract class HttpRemoteService {

  private static final String AUTHORIZATION_HEADER = "Authorization";
  private static final String TOKEN_PREFIX = "Bearer ";

  private Client client;

  protected HttpRemoteService(Client client) {
    this.client = client;
  }

  protected abstract String getServiceUrl();  

  protected WebResource.Builder getSynchronousResource(String resourceUri) {
    return client.resource(getServiceUrl() + resourceUri).type(MediaType.APPLICATION_JSON_TYPE);
  }

  protected WebResource.Builder getSynchronousResource(String resourceUri, String authToken) {
    return getSynchronousResource(resourceUri).header(AUTHORIZATION_HEADER, TOKEN_PREFIX + authToken);
  }

  protected AsyncWebResource.Builder getAsynchronousResource(String resourceUri) {
    return client.asyncResource(getServiceUrl() + resourceUri).type(MediaType.APPLICATION_JSON_TYPE);
  }

  protected AsyncWebResource.Builder getAsynchronousResource(String resourceUri, String authToken) {
    return getAsynchronousResource(resourceUri).header(AUTHORIZATION_HEADER, TOKEN_PREFIX + authToken);
  }

  protected void isAlive() {
    client.resource(getServiceUrl()).get(ClientResponse.class);
  }  

}

and here is how I make it concrete:

private class TestRemoteService extends HttpRemoteService {

    protected TestRemoteService(Client client) {
      super(client);
    }

    @Override
    protected String getServiceUrl() {
      return "http://localhost:8080";
    }

    public Future<TestDTO> get() {
      return getAsynchronousResource("/get").get(TestDTO.class);
    }

    public void post(Object object) {
      getSynchronousResource("/post").post(object);
    }

    public void unavailable() {
      getSynchronousResource("/unavailable").get(Object.class);
    }

    public void authorize() {
      getSynchronousResource("/authorize", "ma token").put();
    }
  }
Natan
  • 2,816
  • 20
  • 37
0

if anyone is trying to use DW 0.8.2 when building a client, and you're getting the following error:

cannot access org.apache.http.config.Registry
class file for org.apache.http.config.Registry not found

at org.apache.maven.plugin.compiler.AbstractCompilerMojo.execute(AbstractCompilerMojo.java:858)
at org.apache.maven.plugin.compiler.CompilerMojo.execute(CompilerMojo.java:129)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:132)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:208)
... 19 more

update your dropwizard-client in your pom.xml from 0.8.2 to 0.8.4 and you should be good. I believe a jetty sub-dependency was updated which fixed it.

    <dependency>
        <groupId>io.dropwizard</groupId>
        <artifactId>dropwizard-client</artifactId>
        <version>0.8.4</version>
        <scope>compile</scope>
    </dependency>
Scott
  • 1,528
  • 1
  • 10
  • 5
-10

You can integrated with Spring Framework to implement