Skip to content
This repository has been archived by the owner on Sep 28, 2022. It is now read-only.

Latest commit

 

History

History
211 lines (157 loc) · 7.44 KB

server-interaction.md

File metadata and controls

211 lines (157 loc) · 7.44 KB

Server Interaction

Pilosa URI

A Pilosa URI has the ${SCHEME}://${HOST}:${PORT} format:

  • Scheme: Protocol of the URI. Default: http.
  • Host: Hostname or ipv4/ipv6 IP address. Default: localhost.
  • Port: Port number. Default: 10101.

All parts of the URI are optional, but at least one of them must be specified. The following are equivalent:

  • http://localhost:10101
  • http://localhost
  • http://:10101
  • localhost:10101
  • localhost
  • :10101

A Pilosa URI is represented by the com.pilosa.client.URI class. Below are a few ways to create URI objects:

import com.pilosa.client.URI;

// create the default URI: http://localhost:10101
URI uri1 = URI.defaultURI();

// create a URI from string address
URI uri2 = URI.address("db1.pilosa.com:20202");

// create a URI with the given host and port
URI uri3 = URI.fromHostPort("db1.pilosa.com", 20202);

Pilosa Client

In order to interact with a Pilosa server, an instance of com.pilosa.client.PilosaClient should be created. The client is thread-safe and uses a pool of connections to the server, so we recommend creating a single instance of the client and reuse it when necessary.

If the Pilosa server is running at the default address (http://localhost:10101) you can create the client with default options using:

PilosaClient client = PilosaClient.defaultClient();

To use a custom server address, you can use the withAddress class method:

PilosaClient client = PilosaClient.withAddress("http://db1.pilosa.com:15000");

If you are running a cluster of Pilosa servers, you can create a Cluster object that keeps addresses of those servers:

Cluster cluster = Cluster.withHost(
    URI.address(":10101"),
    URI.address(":10110"),
    URI.address(":10111")
);

// Create a client with the cluster
PilosaClient client = PilosaClient.withCluster(cluster);

It is possible to customize the behaviour of the underlying HTTP client by passing a ClientOptions object to the withCluster class method:

ClientOptions options = ClientOptions.builder()
    .setConnectTimeout(1000)  // if can't connect in  a second, close the connection
    .setSocketTimeout(10000)  // if no response received in 10 seconds, close the connection
    .setConnectionPoolSizePerRoute(3)  // number of connections in the pool per host
    .setConnectionPoolTotalSize(10)  // number of total connections in the pool
    .setRetryCount(5)  // number of retries before failing the request
    .build();

PilosaClient client = PilosaClient.withCluster(cluster, options);

Once you create a client, you can create indexes, fields and start sending queries.

Here is how you would create a index and field:

Schema schema = client.readSchema();
Index index = schema.index("index");
Field field = index.field("field");
client.syncSchema(schema);

You can send queries to a Pilosa server using the query method of client objects:

QueryResponse response = client.query(field.row(5));

query method accepts an optional argument of type QueryOptions:

QueryOptions options = QueryOptions.builder()
    .setColumns(true)  // return column data in the response
    .build();

QueryResponse response = client.query(field.row(5), options);

Server Response

When a query is sent to a Pilosa server, the server either fulfills the query or sends an error message. In the case of an error, PilosaException is thrown, otherwise a QueryResponse object is returned.

A QueryResponse object may contain zero or more results of QueryResult type. You can access all results using the getResults method of QueryResponse (which returns a list of QueryResult objects),or you can use the getResult method (which returns either the first result or null if there are no results):

QueryResponse response = client.query(field.row(5));

// check that there's a result and act on it
QueryResult result = response.getResult();
if (result != null) {
    // act on the result
}

// iterate over all results
for (QueryResult r : response.getResults()) {
    // act on the result
}

Similarly, a QueryResponse object may include a number of column objects, if setColumns(true) query option was used:

// check that there's a column and act on it
ColumnItem column = response.getColumn();
if (column != null) {
    // act on the column
}

// iterate over all columns
for (ColumnItem column : response.getColumns()) {
    // act on the column
}

QueryResult objects contain:

  • getRow method to retrieve a row result,
  • getCountItems method to retrieve column count per row ID entries returned from TopN queries,
  • getCount method to retrieve the number of rows per the given row ID returned from Count queries.
  • getValue method to retrieve the result of Min, Max or Sum queries.
  • isChanged method returns whether a SetBit or ClearBit query changed a column.
QueryResult result = response.getResult();
RowResult row = result.getRow();
List<Long> columns = row.getColumns();
Map<String, Object> attributes = row.getAttributes();

List<CountResultItem> countItems = result.getCountItems();

long count = result.getCount();

long value = result.getValue();

boolean changed = result.isChanged();

SSL/TLS

Make sure the Pilosa server runs on a TLS address. How To Set Up a Secure Cluster tutorial explains how to do that.

In order to enable TLS support on the client side, the scheme of the address should be explicitly specified as https, e.g.: https://01.pilosa.local:10501

This client library uses the Apache HTTP Library. ClientOptions builder accepts a javax.net.ssl.SSLContext object, which is set to org.apache.http.ssl.SSLContexts.createDefault() by default. If the Pilosa server is using a certificate from a recognized authority, you can use the defaults.

If you are using a self signed certificate, you need to derive from PilosaClient and override the getRegistry method:

public class InsecurePilosaClient extends PilosaClient {

    public InsecurePilosaClient(Cluster cluster, ClientOptions options) {
        super(cluster, options);
    }

    @Override
    protected Registry<ConnectionSocketFactory> getRegistry() {
        HostnameVerifier verifier = new HostnameVerifier() {
            @Override
            public boolean verify(String hostName, SSLSession session) {
                return true;
            }
        };

        SSLContext sslContext = null;
        try {
            sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                    return true;
                }
            }).build();
        } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
            e.printStackTrace();
        }
        if (sslContext == null) {
            throw new RuntimeException("SSL Context not created");
        }

        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
                sslContext,
                new String[]{"TLSv1.2"}, null, verifier);
        return RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslConnectionSocketFactory)
                .build();
    }
}