GridGain Developers Hub
GitHub logo GridGain iso GridGain.com
GridGain Software Documentation

Java Thin Client

Overview

The Java thin client is a lightweight client that connects to the cluster via a standard socket connection. It does not become a part of the cluster topology, never holds any data, and is not used as a destination for compute grid calculations. The thin client simply establishes a socket connection to a standard node​ and performs all operations through that node.

To start a single node cluster that you can use to run examples, refer to the Java Quick Start Guide.

Setting Up

If you use maven or gradle, add the 'ignite-core' dependency to your application:

<properties>
    <gridgain.version>8.7.6</gridgain.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.gridgain</groupId>
        <artifactId>ignite-core</artifactId>
        <version>${gridgain.version}</version>
    </dependency>
</dependencies>
def gridgainVersion = '8.7.6'

dependencies {
    compile group: 'org.gridgain', name: 'ignite-core', version: gridgainVersion
}

Alternatively, you can use the ignite-core-8.7.6.jar library from the GridGain distribution package.

Connecting to Cluster

To initialize a thin client, use the Ignition.startClient(ClientConfiguration) method. The method accepts a ClientConfiguration object, which defines client connection parameters.

The method returns the IgniteClient interface, which provides various methods for accessing data. IgniteClient is an auto-closable resource. Use the try-with-resources statement to close the thin client and release the resources associated with the connection.

ClientConfiguration cfg = new ClientConfiguration().setAddresses("127.0.0.1:10800");
try (IgniteClient client = Ignition.startClient(cfg)) {
    ClientCache<Integer, String> cache = client.cache("myCache");
    // get data from the cache
}

You can provide addresses of multiple nodes. In this case, the thin client will randomly try all the servers in the list and will throw ClientConnectionException if none is available.

try (IgniteClient client = Ignition.startClient(new ClientConfiguration().setAddresses("node1_address:10800",
        "node2_address:10800", "node3_address:10800"))) {
} catch (ClientConnectionException ex) {
    // All the servers are unavailable
}

Please note that the code above provides a failover mechanism in case of server node failures but with a caveat that cache queries may return duplicate results. Refer to the Handling Node Failures section for further information.

Using Key-Value API

Java thin client suports most of the key-value operations available in the thick client. To execute key-value operations on a specific cache, get an instance of the cache and use one of its methods.

Getting a Cache Instance

The key-value API is provided by the ClientCache interface. You can use the following methods to obtain an instance of ClientCache:

  • IgniteClient.cache(String): assumes a cache with the specified name exists. The method does not communicate with the cluster to check if the cache really exists. Subsequent cache operations will fail if the cache does not exist.

  • IgniteClient.getOrCreateCache(String), IgniteClient.getOrCreateCache(ClientCacheConfiguration): get existing cache with the specified name or create the cache if it does not exist. The former operation creates a cache with default configuration.

  • IgniteClient.createCache(String), IgniteClient.createCache(ClientCacheConfiguration): create a cache with the specified name and fail if the cache already exists.

Use IgniteClient.cacheNames() to list all existing caches.

ClientCacheConfiguration cacheCfg = new ClientCacheConfiguration().setName("References")
        .setCacheMode(CacheMode.REPLICATED)
        .setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC);

ClientCache<Integer, String> cache = client.getOrCreateCache(cacheCfg);

Basic Cache Operations

The following code snippet demonstrates how to execute basic cache operations from the thin client.

Map<Integer, String> data = IntStream.rangeClosed(1, 100).boxed()
        .collect(Collectors.toMap(i -> i, Object::toString));

cache.putAll(data);

assert !cache.replace(1, "2", "3");
assert "1".equals(cache.get(1));
assert cache.replace(1, "1", "3");
assert "3".equals(cache.get(1));

cache.put(101, "101");

cache.removeAll(data.keySet());
assert cache.size() == 1;
assert "101".equals(cache.get(101));

cache.removeAll();
assert 0 == cache.size();

Executing Scan Queries

Use a ScanQuery<K, V> to get a set of entries that satisfy a given condition. The thin client sends the query to the cluster node where it is executed as a normal scan query.

The query condition is specified by an IgniteBiPredicate<K, V> object that is passed to the query constructor as an argument. The predicate is applied on the server side. If you don’t provide any predicate, the query will return all cache entries.

The results of the query are transferred to the client page by page. Each page contains a specific number of entries and is fetched to the client only when the entries from that page are requested. To change the number of entries in a page, use the ScanQuery.setPageSize(int pageSize) method (default value is 1024).

ClientCache<Integer, Person> personCache = client.getOrCreateCache("personCache");

Query<Cache.Entry<Integer, Person>> qry = new ScanQuery<Integer, Person>(
        (i, p) -> p.getName().contains("Smith"));

try (QueryCursor<Cache.Entry<Integer, Person>> cur = personCache.query(qry)) {
    for (Cache.Entry<Integer, Person> entry : cur) {
        // Process the entry ...
    }
}

The IgniteClient.query(…​) method returns an instance of FieldsQueryCursor. Make sure to always close the cursor after you obtain all results.

Working with Binary Objects

The thin client fully supports Binary Object API described in the Working with Binary Objects section. Use CacheClient.withKeepBinary() to switch the cache to binary mode and start working directly with binary objects avoiding serialization/deserialization. Use IgniteClient.binary() to get an instance of IgniteBinary and build an object from scratch.

IgniteBinary binary = client.binary();

BinaryObject val = binary.builder("Person").setField("id", 1, int.class).setField("name", "Joe", String.class)
        .build();

ClientCache<Integer, BinaryObject> cache = client.cache("persons").withKeepBinary();

cache.put(1, val);

BinaryObject value = cache.get(1);

Refer to the Working with Binary Objects page for detailed information.

Executing SQL Statements

The Java thin client provides a SQL API to execute SQL statements. SQL statements are declared using the SqlFieldsQuery objects and executed through the IgniteClient.query(SqlFieldsQuery) method.

client.query(new SqlFieldsQuery(String.format(
        "CREATE TABLE IF NOT EXISTS Person (id INT PRIMARY KEY, name VARCHAR) WITH \"VALUE_TYPE=%s\"",
        Person.class.getName())).setSchema("PUBLIC")).getAll();

int key = 1;
Person val = new Person(key, "Person 1");

client.query(new SqlFieldsQuery("INSERT INTO Person(id, name) VALUES(?, ?)").setArgs(val.getId(), val.getName())
        .setSchema("PUBLIC")).getAll();

FieldsQueryCursor<List<?>> cursor = client
        .query(new SqlFieldsQuery("SELECT name from Person WHERE id=?").setArgs(key).setSchema("PUBLIC"));

//get the results; the `getAll()` methods closes the cursor; you do not have to call cursor.close();
List<List<?>> results = cursor.getAll();

results.stream().findFirst().ifPresent(columns -> {
    System.out.println("name = " + columns.get(0));
});

The query(SqlFieldsQuery) method returns a FieldsQueryCursor that can be used to iterate over the results. The cursor must be closed to release the resources associated with it.

Read more about using SqlFieldsQuery and SQL API in the Using SQL API section.

Handling Exceptions

Handling Node Failures

When you provide the addresses of multiple nodes in the client configuration, the client automatically switches to the next node if the current connection fails and retries any ongoing operation.

In case of atomic operations, the failover to another node is transparent to the user. However, if you execute a scan query or a SELECT query, it may return duplicate results. This can happen because queries return data in pages, and if the node that the client is connected to goes down while the client retrieves the pages, the client connects to another node and executes the query again.

To avoid this, you have to write some code in your application that checks if entries returned by the client are duplicated. Consider the following code:

Query<Cache.Entry<Integer, Person>> qry = new ScanQuery<Integer, Person>(
        (i, p) -> p.getName().contains("Smith"));

try (QueryCursor<Cache.Entry<Integer, Person>> cur = cache.query(qry)) {
    // collecting the results into a map removes the duplicates
    Map<Integer, Person> res = cur.getAll().stream()
            .collect(Collectors.toMap(Cache.Entry::getKey, Cache.Entry::getValue));
}

Security

SSL/TLS

To use encrypted communication between the thin client and the cluster, you have to enable it both in the cluster configuration and the client configuration. Refer to the Enabling SSL/TLS for Thin Clients section for the instruction on the cluster configuration.

To enable encrypted communication in the thin client, provide a keystore that contains the encryption key and a truststore with the trusted certificates in the thin client configuration.

ClientConfiguration clientCfg = new ClientConfiguration().setAddresses("127.0.0.1:10800");

clientCfg.setSslMode(SslMode.REQUIRED).setSslClientCertificateKeyStorePath(KEYSTORE)
        .setSslClientCertificateKeyStoreType("JKS").setSslClientCertificateKeyStorePassword("123456")
        .setSslTrustCertificateKeyStorePath(TRUSTSTORE).setSslTrustCertificateKeyStorePassword("123456")
        .setSslTrustCertificateKeyStoreType("JKS").setSslKeyAlgorithm("SunX509").setSslTrustAll(false)
        .setSslProtocol(SslProtocol.TLS);

try (IgniteClient client = Ignition.startClient(clientCfg)) {
    //...
}

The following table explains encryption parameters of the client configuration:

Parameter Description Default Value

sslMode

Either REQURED or DISABLED.

DISABLED

sslClientCertificateKeyStorePath

The path to the keystore file with the private key.

N/A

sslClientCertificateKeyStoreType

The type of the keystore.

JKS

sslClientCertificateKeyStorePassword

The password to the keystore.

N/A

sslTrustCertificateKeyStorePath

The path to the truststore file.

N/A

sslTrustCertificateKeyStoreType

The type of the truststore.

JKS

sslTrustCertificateKeyStorePassword

The password to the truststore.

N/A

sslKeyAlgorithm

Sets the key manager algorithm that will be used to create a key manager. Note that in most cases the default value work well. However, on the Android platform, this value need to be set to X509.

SunX509

sslTrustAll

If this parameter is set to true, certificates will not be validated.

N/A

sslProtocol

The name of the protocol that will be used for data encryption.

TLS

Authentication

Configure authentication on the cluster side and simply provide the user name and password in the client configuration.

ClientConfiguration clientCfg = new ClientConfiguration().setAddresses("127.0.0.1:10800").setUserName("joe")
        .setUserPassword("passw0rd!");

try (IgniteClient client = Ignition.startClient(clientCfg)) {
    // ...
} catch (ClientAuthenticationException e) {
    // Handle authentication failure
}

Authorization

Thin client authorization can be configured in the cluster. Refer to the Authorization page for details.