Getting Started with the Hazelcast Java Client

What You’ll Learn

This tutorial will get you started with the Hazelcast Java client and manipulate a map.

Before you Begin

Before starting this tutorial, make sure the following prerequisites are met:

Start a Hazelcast Viridian Cloud Cluster

  1. Sign up for a Hazelcast Viridian Cloud account (free trial is available).

  2. Log in to your Hazelcast Viridian Cloud account and start your trial by filling in the welcome questionnaire.

  3. A Viridian cluster will be created automatically when you start your trial.

  4. Press the Connect Cluster dialog and switch over to the Advanced setup tab for connection information needed below.

  5. From the Advanced setup tab, download the keystore files and take note of your Cluster ID, Discovery Token and Password as you will need them later.

Setup a Hazelcast Client

Create a new folder and navigate to it:

mkdir hazelcast-java-example
cd hazelcast-java-example

Download the latest version of Hazelcast Enterprise zip slim from here and extract the Hazelcast Enterprise jar into this directory:

hazelcast-enterprise-5.3.1.jar

Extract the keystore files you downloaded from Viridian into this directory. The files you need for this tutorial are:

client.keystore
client.pfx
client.truststore

To understand and use the client, review the Java client documentation to better understand what is possible.

Understanding the Java Client

The following section creates and starts a Hazelcast client with default configuration, connects to your Viridian cluster before shutting the client down at the end.

Create a Java file named “Example.java” and put the following code inside it:

import java.util.Properties;

import com.hazelcast.client.HazelcastClient;
import com.hazelcast.client.config.ClientConfig;
import com.hazelcast.config.SSLConfig;
import com.hazelcast.core.HazelcastInstance;

public class Example {

    public static void main(String[] args) throws Exception {
        ClientConfig config = new ClientConfig();

        // Your Viridian cluster name.
        config.setClusterName("<YOUR_CLUSTER_ID>");

        // Your discovery token to connect Viridian cluster.
        config.getNetworkConfig().getCloudConfig()
                .setDiscoveryToken("<YOUR_DISCOVERY_TOKEN>")
                .setEnabled(true);

        // Configure SSL
        ClassLoader classLoader = ClientWithSsl.class.getClassLoader();
        Properties props = new Properties();
        props.setProperty("javax.net.ssl.keyStore", classLoader.getResource("client.keystore").toURI().getPath());
        props.setProperty("javax.net.ssl.keyStorePassword", "<YOUR_CERTIFICATE_PASSWORD>");
        props.setProperty("javax.net.ssl.trustStore",
                classLoader.getResource("client.truststore").toURI().getPath());
        props.setProperty("javax.net.ssl.trustStorePassword", "<YOUR_CERTIFICATE_PASSWORD>");
        config.getNetworkConfig().setSSLConfig(new SSLConfig().setEnabled(true).setProperties(props));

        // Create client
        HazelcastInstance client = HazelcastClient.newHazelcastClient(config);

        System.out.println("Welcome to your Hazelcast Viridian Cluster!")

        client.shutdown();
    }
}

Understanding the Hazelcast SQL API

Hazelcast SQL API is a Calcite SQL based interface to allow you to interact with Hazelcast much like any other datastore.

In the following example, we will create a map and insert into it, entries where the keys are ids and the values are defined as an object representing a city.

import java.util.Properties;

import com.hazelcast.client.HazelcastClient;
import com.hazelcast.client.config.ClientConfig;
import com.hazelcast.config.SSLConfig;
import com.hazelcast.core.HazelcastInstance;

import com.hazelcast.nio.serialization.compact.CompactReader;
import com.hazelcast.nio.serialization.compact.CompactSerializer;
import com.hazelcast.nio.serialization.compact.CompactWriter;
import com.hazelcast.sql.SqlResult;
import com.hazelcast.sql.SqlRow;
import com.hazelcast.sql.SqlService;

public class Example {

    public final class CityDTO {

        private final String country;

        private final String city;

        private final int population;

        public CityDTO(String country, String city, int population) {
            this.country = country;
            this.city = city;
            this.population = population;
        }

        public String getCountry() {
            return country;
        }

        public String getCity() {
            return city;
        }

        public int getPopulation() {
            return population;
        }
    }

    public final class CitySerializer implements CompactSerializer<CityDTO> {
        @Override
        public CityDTO read(CompactReader compactReader) {
            return new CityDTO(compactReader.readString("country"),
                            compactReader.readString("city"),
                            compactReader.readInt32("population"));
        }

        @Override
        public void write(CompactWriter compactWriter, CityDTO city) {
            compactWriter.writeString("country", city.getCountry());
            compactWriter.writeString("city", city.getCity());
            compactWriter.writeInt32("population", city.getPopulation());
        }

        @Override
        public String getTypeName() {
            return "CityDTO";
        }

        @Override
        public Class<CityDTO> getCompactClass() {
            return CityDTO.class;
        }
    }

    public static void main(String[] args) throws Exception {
        ClientConfig config = new ClientConfig();

        // Connection details for cluster
        config.setClusterName("<YOUR_CLUSTER_ID>");

        config.getNetworkConfig().getCloudConfig()
                .setDiscoveryToken("<YOUR_DISCOVERY_TOKEN>")
                .setEnabled(true);

        ClassLoader classLoader = Example.class.getClassLoader();
        Properties props = new Properties();
        props.setProperty("javax.net.ssl.keyStore", classLoader.getResource("client.keystore").toURI().getPath());
        props.setProperty("javax.net.ssl.keyStorePassword", "<YOUR_CERTIFICATE_PASSWORD>");
        props.setProperty("javax.net.ssl.trustStore", classLoader.getResource("client.truststore").toURI().getPath());
        props.setProperty("javax.net.ssl.trustStorePassword", "<YOUR_CERTIFICATE_PASSWORD>");
        config.getNetworkConfig().setSSLConfig(new SSLConfig().setEnabled(true).setProperties(props));

        // Register Compact Serializers
        config.getSerializationConfig().getCompactSerializationConfig()
            .addSerializer(new Example().new CitySerializer());

        // Connect to your Hazelcast Cluster
        HazelcastInstance client = HazelcastClient.newHazelcastClient(config);

        try {
            // Create a map on the cluster
            createMapping(client.getSql());

            // Add some data
            insertCities(client);

            // Output the data
            fetchCities(client.getSql());

        } finally {
            client.shutdown();
        }
    }

    private static void createMapping(SqlService sqlService) {
        // See: https://docs.hazelcast.com/hazelcast/latest/sql/mapping-to-maps#compact-objects
        System.out.print("\nCreating mapping...");

        String mappingSql = ""
                + "CREATE OR REPLACE MAPPING cities("
                + "     __key INT,"
                + "     country VARCHAR,"
                + "     city VARCHAR,"
                + "     population INT"
                + ") TYPE IMap"
                + " OPTIONS ("
                + "     'keyFormat' = 'int',"
                + "     'valueFormat' = 'compact',"
                + "     'valueCompactTypeName' = 'Example$CityDTO'"
                + " )";

        try (SqlResult ignored = sqlService.execute(mappingSql)) {
            System.out.print("OK.");
        } catch (Exception ex) {
            System.out.print("FAILED. " + ex.getMessage());
        }
    }

    private static void insertCities(HazelcastInstance client) {
        try {
            String deleteQuery = "DELETE from cities";

            String insertQuery = "INSERT INTO cities "
                + "(__key, city, country, population) VALUES"
                + "(1, 'London', 'United Kingdom', 9540576),"
                + "(2, 'Manchester', 'United Kingdom', 2770434),"
                + "(3, 'New York', 'United States', 19223191),"
                + "(4, 'Los Angeles', 'United States', 3985520),"
                + "(5, 'Istanbul', 'Türkiye', 15636243),"
                + "(6, 'Ankara', 'Türkiye', 5309690),"
                + "(7, 'Sao Paulo ', 'Brazil', 22429800)";

            System.out.print("\nInserting data...");
            client.getSql().execute(deleteQuery);
            client.getSql().execute(insertQuery);
            System.out.print("OK.");
        } catch (Exception ex) {
            System.out.print("FAILED. " + ex.getMessage());
        }
    }

    private static void fetchCities(SqlService sqlService) {
        System.out.print("\nFetching cities...");

        try (SqlResult result = sqlService.execute("SELECT __key, this FROM cities")) {
            System.out.print("OK.\n");
            System.out.println("--Results of 'SELECT __key, this FROM cities'");

            System.out.printf("%4s | %20s | %20s | %15s |%n", "id", "country", "city", "population");
            for (SqlRow row : result) {
                int id = row.getObject("__key");
                CityDTO cityDTO = row.getObject("this");
                System.out.printf("%4s | %20s | %20s | %15s |%n",
                        id,
                        cityDTO.getCountry(),
                        cityDTO.getCity(),
                        cityDTO.getPopulation()
                );
            }
        } catch (Exception ex) {
            System.out.print("FAILED. " + ex.getMessage());
        }
    }
}

The output of this code is given below:

Creating the mapping...OK.
Inserting data...OK.
Fetching cities...OK.
--Results of 'SELECT __key, this FROM cities'
|   id | country              | city                 | population      |
|    2 | United Kingdom       | Manchester           | 2770434         |
|    6 | Türkiye              | Ankara               | 5309690         |
|    1 | United Kingdom       | London               | 9540576         |
|    7 | Brazil               | Sao Paulo            | 22429800        |
|    4 | United States        | Los Angeles          | 3985520         |
|    5 | Türkiye              | Istanbul             | 15636243        |
|    3 | United States        | New York             | 19223191        |
Ordering of the keys is NOT enforced and results may NOT correspond to insertion order.

Understanding the Hazelcast IMap API

A Hazelcast Map is a distributed key-value store, similar to Python dictionary. You can store key-value pairs in a Hazelcast Map.

In the following example, we will work with map entries where the keys are ids and the values are defined as a string representing a city name.

import java.util.Map;
import java.util.Properties;
import java.util.Set;

import com.hazelcast.client.HazelcastClient;
import com.hazelcast.client.config.ClientConfig;
import com.hazelcast.config.SSLConfig;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;

public class Example {

    public static void main(String[] args) throws Exception {
        ClientConfig config = new ClientConfig();

        // Your Viridian cluster name.
        config.setClusterName("<YOUR_CLUSTER_ID>");

        // Your discovery token to connect Viridian cluster.
        config.getNetworkConfig().getCloudConfig()
                .setDiscoveryToken("<YOUR_DISCOVERY_TOKEN>")
                .setEnabled(true);

        // Configure SSL
        ClassLoader classLoader = ClientWithSsl.class.getClassLoader();
        Properties props = new Properties();
        props.setProperty("javax.net.ssl.keyStore", classLoader.getResource("client.keystore").toURI().getPath());
        props.setProperty("javax.net.ssl.keyStorePassword", "<YOUR_CERTIFICATE_PASSWORD>");
        props.setProperty("javax.net.ssl.trustStore", classLoader.getResource("client.truststore").toURI().getPath());
        props.setProperty("javax.net.ssl.trustStorePassword", "<YOUR_CERTIFICATE_PASSWORD>");
        config.getNetworkConfig().setSSLConfig(new SSLConfig().setEnabled(true).setProperties(props));

        // Create client
        HazelcastInstance client = HazelcastClient.newHazelcastClient(config);

        try {
            // Create a map on the cluster
            IMap<Integer, String> citiesMap = client.getMap("cities");

            // Clear the map
            citiesMap.clear();

            // Add some data
            citiesMap.put(1, "London");
            citiesMap.put(2, "New York");
            citiesMap.put(3, "Tokyo");

            // Output the data
            Set<Map.Entry<Integer, String>> entries = citiesMap.entrySet();

            for (Map.Entry<Integer, String> entry : entries)
            {
                System.out.println(entry.getKey() + " -> " + entry.getValue() );
            }
        } finally {
            client.shutdown();
        }
    }
}

Following line returns a map proxy object for the cities map:

            // Create a map on the cluster
            IMap<Integer, String> citiesMap = client.getMap("cities");

If cities doesn’t exist, it will be automatically created. All the clients connected to the same cluster will have access to the same map.

With these lines, client adds data to the cities map. The first parameter is the key of the entry, the second one is the value.

            // Add some data
            citiesMap.put(1, "London");
            citiesMap.put(2, "New York");
            citiesMap.put(3, "Tokyo");

Then, we get the data using the entrySet() method and iterate over the results.

            // Output the data
            Set<Map.Entry<Integer, String>> entries = citiesMap.entrySet();

            for (Map.Entry<Integer, String> entry : entries)
            {
                System.out.println(entry.getKey() + " -> " + entry.getValue() );
            }

The output of this code is given below:

2 -> New York
1 -> London
3 -> Tokyo
Ordering of the keys is NOT enforced and results may NOT correspond to entry order.

Summary

In this tutorial, you learned how to get started with the Hazelcast Java Client, connect to a Viridian instance and put data into a distributed map.

See Also

There are a lot of things that you can do with the Java Client. For more, such as how you can query a map with predicates and SQL, check out our Hazelcast repository and our Java client documentation to better understand what is possible.

If you have any questions, suggestions, or feedback please do not hesitate to reach out to us via Hazelcast Community Slack. Also, please take a look at the issue list if you would like to contribute to the client.