Skip to main content
Skip table of contents

Writing a Greeter Client in Java

This page presents a step-by-step guide to writing the client-side of our Java Greeter application.

This client creates a proxy to a remote object that implements the Greeter interface and invokes the greet operation on this object.

You can find the complete source code for this example in the ice-demos repository.

Client Implementation

The structure of our client is going to look like:

JAVA
package com.example.ice.greeter.client;

import com.example.visitorcenter.GreeterPrx;
import com.zeroc.Ice.Communicator;
import com.zeroc.Ice.Util;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

class Client {
    public static void main(String[] args) {
        // ...
    }
}

Before anything else, we import the following packages:

  • com.example.visitorcenter: The package that the Slice compiler (slice2java) generated from Greeter.ice.

  • com.zeroc.Ice: This package provides types that are necessary for accessing the Ice runtime.

  • java.util.concurrent: Standard library types we use in this client.

Then we get to the interesting part: the main method which runs the client logic. This logic can be broken down into four pieces:

1. Create a Communicator

First we create a Communicator with Util.initialize:

JAVA
try (Communicator communicator = Util.initialize(args)) {
    // ... client code
}

The communicator is the main entry point into the Ice runtime. Its responsibilities include establishing connections to servers, caching these connections, and managing configuration properties. We also need a communicator to create a proxy (see next step).

Our client, like most Ice applications, creates a single communicator.

It is important to make sure that your communicator is properly closed when no longer needed. This ensures that network connections are gracefully closed, threads are joined, and other important clean-up occurs. The easiest way to do this is with a try-with-resources statement like we do here.

2. Create a Greeter Proxy

Next, we need a way to call on a remote Greeter object. In Ice, this is done with proxies. Proxies are local constructs that represent remote Ice objects and provide methods to call operations on those objects.

We create a Greeter proxy by calling createProxy on the GreeterPrx interface generated by the Slice compiler. This returns a new instance of type GreeterPrx (a Greeter proxy):

JAVA
var greeter = GreeterPrx.createProxy(
    communicator, "greeter:tcp -h localhost -p 4061");

createProxy accepts our communicator and a “stringified proxy” with the address of the remote Ice object. Here, our stringified proxy says that the target Ice object is named “greeter” and can be reached via tcp on localhost on port 4061.

The name of the interface (Greeter) and the identity of the Ice object (greeter) are independent. The Ice objects hosted in the server could just as easily have identities like santa, bugsBunny, etc.

3. Make an Invocation

The third step is to call greet on the remote Ice object using our proxy and to print the greeting:

JAVA
String greeting = greeter.greet(System.getProperty("user.name"));
System.out.println(greeting);

The greet method does all the heavy lifting for us: the proxy creates a request with the username string, the communicator establishes a connection to localhost:4061, and the request is sent over it. When a response is received, the proxy will unmarshal its payload and finally return a string (the greeting).

Here, we called the synchronous version of greet, which means this method call will block until the response is received. And don’t let the simplicity of the syntax fool you: this is a remote call which will be much slower than a local call!

You can instead class greet asynchronously with greetAsync on our proxy like this:

JAVA
CompletableFuture<String> futureGreeting = greeter.greetAsync("alice");

try {
    greeting = futureGreeting.get();
    System.out.println(greeting);
} catch (InterruptedException | ExecutionException e) {
    System.out.println("Could not get greeting: " + e.getMessage());
}

With the asynchronous version, the call doesn’t block. Instead the method returns a CompletableFuture which can be polled for completion, as shown above.

Asynchronous invocations are more semantically correct for remote calls, and they alert readers to the potential delays inherent to these calls. But in Java they’re slightly more complicated to write… as a result, the best invocation syntax depends on your situation.

4. Cleanup

The final step is the end of our main method. At this point, our communicator goes out of scope and it is closed automatically (because we used a try-with-resources statement), and then our application exits.

Running the Client

After building the client (see the demo’s README for instructions), you can run it with gradle:

BASH
./gradlew :client:run --quiet

This client won’t work unless you’ve also launched a Greeter server.
Writing and running a Greeter server is covered on the next page.

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.