Jetty Http Client

From Blazegraph
Jump to: navigation, search

This page describes the Java-based interface to the NanoSparqlServer and its configuration.

Configuration

RemoteRepository

The RemoteRepository the main entry point for the java based SPARQL client. This interface also supports the various extension methods defined by the NanoSparqlServer. The RemoteRepositoryManager provides access to the multi-tenancy API features (create, list, destroy, query, etc). The RemoteRepository may also be wrapped using the BigdataSailRemoteRepository.

HttpClient

The RemoteRepository uses a jetty HttpClient implementation. A series of factory patterns is used to configure that jetty HttpClient class:

  • HttpClientConfigurator - specifies the IHttpClientFactory
  • DefaultHttpClientFactory - the default IHttpClientFactory implementation. This class as an inner Options interface that allows you to configure the behavior of the HttpClient, including the following. Additional options may be added from time to time, so please check the source code for other options. These options may be specified using an environment variable (-Dname=value).
    • The SSL keystore path (no default, which is interpreted as trust everyone).
    • Whether the client will follow redirects (defaults to true).
    • The http request buffer size.
    • The http response buffer size.

Background

Some users have experienced connection problems with the Apache HttpClient, particularly when making multiple concurrent requests. Typically an EOFException would be thrown when trying to retrieve a response.

By using the Jetty HttpClient instead, the same test code that reliably failed now passes.

Therefore releases after 1.4.0 use jetty for the http client. This provides good performance (equivalent to the Apache http client code in our own testing) and better http protocol support (including several nasty protocol layer errors that appear with the Apache client, but not with jetty and not with curl).

The Jetty HttpClient

There are a few differences that are worth noting.

1) The client must be started and stopped using the start() and stop() methods.

2) The access to the response stream is via a ResponseListener instance that is passed to the client.

AutoCloseHttpClient

To simplify the management of the HttpClient an AutoCloseable implementing variant is provided.

The RemoteRepositoryManager will close any AutoCloseHttpClient when it is closed.

Apache Entities

The integration is somewhat complicated by the standard Jetty client not implementing multipart content. The solution implemented is to define a Jetty EntityContentProvider that wraps a standard Apache Entity. With this pattern we can use the richer Apache content definitions and present the data to the Jetty client.

SSL Authorization

The Jetty HttpClient authorization uses the SslContextFactory class.

This can be used with two principle trust patterns:

  1. Trust everyone
  2. Trust known sources

Trust Everyone

new SslContextFactory(true/*trust evryone*/);

Trust Known Sources

new SslContextFactory(filepath_to_key_store);

Where the path to the store with the trusted keys is provided.

Initializing Jetty HttpClient

The standard AutoCloseHttpClient constructor requires an SslContextFacotry to be provided:

 AutoCloseHttpClient client = new AutoCloseHttpClent(new SslContextFactory(true/*trust all*/));

Client Scope

Since the client must be started and stopped, this can be managed either explicitly by the caller:

final AutoCloseHttpClient client = new AutoCloseHttpClient((new SslContextFactory(true/*trust all*/));
client.start();
try {
   ...
} finally {
    client.close();
}

or by the RemoteRepositoryManager:

final AutoCloseHttpClient client = new AutoCloseHttpClient((new SslContextFactory(true/*trust all*/));
client.start();
final RemoteRepositoryManager repo = new RemoteRepositoryManager(url, true /*useLBS*/, client, executorService);
try {
    ...
} finally {
    repo.close();
}

Sample Code

To connect and make a simple status request:

String getStatus(final String serviceURL, final ExecutorService executor) throws HttpException {
   final RemoteRepositoryManager rpm = new RemoteRepositoryManager(serviceURL, executor);
   try {
      final ConnectOptions opts = new ConnectOptions(serviceURL + "/status");
      opts.method = "GET";

      final JettyResponseListener response = rpm.doConnect(opts);
      rpm.checkResponseCode(response); // can throw HttpException

      return response.getResponseBody();
   } finally {
      repo.close();
   }
}

To add statements to a namespace:

String addStatements(final String serviceURL, final ExecutorService executor, final String namespace, final List<Statement> statements)
    throws HttpException 
{
   final RemoteRepositoryManager rpm = new RemoteRepositoryManager(serviceURL, executor);
   try {
      rpm.getRepositoryForNamespace(namespace).add(new RemoteRepository.AddOp(statements));
   } finally {
      repo.close();
   }
}

To retrieve statements:

String getStatements(final String serviceURL, final ExecutorService executor, final String namespace)
    throws HttpException 
{
   final RemoteRepositoryManager repo = new RemoteRepositoryManager(serviceURL, executor);
   try {
      return rpm.getRepositoryForNamespace(namespace).prepareTupleQuery("SELECT * {?s ?p ?o} LIMIT 100").evaluate());
   } finally {
      repo.close();
   }
}