Skip to main content

Use the Lattice SDK for gRPC in Java

This page shows how you can install, set up, and use the Lattice SDK for gRPC in Java to get information about an entity in your environment.

Click to go to the gRPC SDK repository. By downloading and/or using, you agree to the terms of use. If you do not agree, do not use the SDK.

Before you begin

Before you install and use the gRPC SDK in Java, do the following:

  1. Complete the set up steps to generate a valid API bearer token and publish at least one entity to your environment.
  2. Install a Java Development Kit (JDK). We recommend using the latest version, for example AdoptOpenJDK.
  3. Install the latest Gradle distribution.

Set up your Java project

For this example, set up a new Java Gradle project. In the following steps, you'll replace the starter code with examples to help you get started with the Lattice SDK.

  1. Confirm that you have Gradle installed:

    gradle --version
  2. Initialize a sample Gradle project:

    gradle init
  3. Choose the following options when prompted and leave the rest as defaults:

    1. Application - for build type.

    1. Java - for implementation language.

    8 - or higher for target Java version.

    2. Groovy - for DSL option.

This generates a new Groovy project. Next, you'll replace the existing boilerplate code with examples that interact with your Lattice environment.

Get the example files

Copy and paste the following examples into your Java project:

MyEntityManagerClient.java
 // You might need to modify this statement depending on the location of your file relative to the project
package org.example;

import java.util.concurrent.CountDownLatch;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.anduril.entitymanager.v1.EntityManagerAPIGrpc;
import com.anduril.entitymanager.v1.EntityManagerAPIGrpc.EntityManagerAPIStub;
import com.anduril.entitymanager.v1.GetEntityRequest;
import com.anduril.entitymanager.v1.GetEntityResponse;

import io.grpc.ChannelCredentials;
import io.grpc.ClientInterceptor;
import io.grpc.Grpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.TlsChannelCredentials;
import io.grpc.stub.MetadataUtils;
import io.grpc.stub.StreamObserver;
import io.grpc.util.AdvancedTlsX509TrustManager;
import io.grpc.util.AdvancedTlsX509TrustManager.Verification;

/**
* This class represents a client for interacting with the EntityManager API.
* It uses gRPC to communicate with the server and provides methods for retrieving entities.
*/
public class MyEntityManagerClient {

/**
* The stub for the EntityManagerAPI. This is the client-side implementation of the API interface.
*/
private EntityManagerAPIStub serviceStub;

/**
* Constructs a new instance of the client.
* Creates a channel to connect to the server using the URL of the host.
* Creates an interceptor to apply authorization headers to requests.
* Initializes the service stub with the channel and interceptor.
*/
public MyEntityManagerClient(){
// Creates a channel object to connect to Lattice using your environment's URL
ManagedChannel channel = ManagedChannelBuilder.forAddress("$YOUR_LATTICE_URL", 443).useTransportSecurity().build();

// Creates a metadata object to apply an authorization header to requests.
Metadata header = new Metadata();
Metadata.Key<String> key = Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER);
header.put(key, "Bearer $YOUR_BEARER_TOKEN");

// Creates an interceptor to apply authorization headers to requests.
ClientInterceptor interceptor = MetadataUtils.newAttachHeadersInterceptor(header);

// Initializes the service stub with the channel and interceptor.
this.serviceStub = EntityManagerAPIGrpc.newStub(channel).withInterceptors(interceptor);
}

/**
* Retrieves an entity with the given ID from the server.
* It uses a CountDownLatch to wait for the response to be received before returning.
*
* @param entityId The ID of the entity to retrieve.
* @throws InterruptedException If the thread is interrupted while waiting for the response.
*/
public void getEntity(String entityId) throws InterruptedException {
// Creates a CountDownLatch with an initial count of 1.
// This latch will be used to wait for the response to be received before returning.
final CountDownLatch finishLatch = new CountDownLatch(1);

// Creates a StreamObserver to handle the response from the server.
StreamObserver<GetEntityResponse> responseObserver = new StreamObserver<GetEntityResponse>() {
@Override
public void onNext(GetEntityResponse value) {
// Print the response to the console.
System.out.println(value);

// Count down the latch, indicating that a response has been received.
finishLatch.countDown();
}

@Override
public void onError(Throwable t) {
// Print the error to the console.
t.printStackTrace();

// Count down the latch, indicating that an error has occurred.
finishLatch.countDown();
}

@Override
public void onCompleted() {
// Print a message to the console indicating that the response stream has been closed.
System.out.println("Finished receiving data from server");

// Count down the latch, indicating that the response stream has been closed.
finishLatch.countDown();
}
};

// Creates a request to retrieve the entity with the given ID.
GetEntityRequest request = GetEntityRequest.newBuilder().setEntityId(entityId).build();

// Sends the request to the server and passes in the response observer to handle the response.
this.serviceStub.getEntity(request, responseObserver);

// Waits for the response to be received before returning.
finishLatch.await();
}
}
App.java
 package org.example;
import org.example.MyEntityManagerClient;

public class App {
/**
* The main method of the application.
* Creates a new instance of MyEntityManagerClient and calls its getEntity method to retrieve an entity.
* It then prints the entity ID to the console.
*/
public static void main(String[] args){
MyEntityManagerClient entityManager = new MyEntityManagerClient();
try {
// Replace $ENTITY_ID with any entity from your active Lattice environment.
entityManager.getEntity("$ENTITY_ID");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

Your project structure should now look similar to the following:

├── gradle 
│ ├── libs.versions.toml
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── app
├── build.gradle
└── src
├── main
│ └── java
│ └── org
│ └── example
│ ├──App.Java
│ └──MyEntityManagerClient.java
└── test
└── java
└── org
└── example
└──AppTest.Java
info
  • In MyEntityManagerClient.java, replace $YOUR_LATTICE_URL and $YOUR_BEARER_TOKEN with your information.
  • In App.java, replace $ENTITY_ID with the entity you published when you previously set up your development environment.

Install the SDK

To install the Lattice SDK in Java, add the following to your app/build.gradle file.

build.gradle
.
.
.
dependencies {
// For making gRPC requests
implementation 'io.grpc:grpc-netty-shaded:1.65.1'
implementation 'io.grpc:grpc-stub:1.65.1'
implementation 'io.grpc:grpc-util:1.65.1'

// Generic proto definitions
implementation 'com.google.protobuf:protobuf-java:4.27.4'
implementation 'com.google.protobuf:protobuf-java-util:4.27.4'

// The Lattice Java SDK
implementation 'com.anduril:lattice-sdk:1.2.2'
}

Run the application

Use the run command to build the project and connect to your Lattice environment.

./gradlew run

If the request is successful and you replaced the entity ID then you should see the entity object you created when setting up your development environment. If you did not change the entity ID to a valid, existing entity, then you will get the following error:

ConnectError: [not_found] entity not found ENTITY_ID

This means you successfully authenticated and completed the call to your environment, but the entity does not exist. This is an expected error. If you get any other error then there is a problem with your code. Check that your Lattice environment URL and our bearer token are correct.

How it works

In MyEntityManagerClient.java you implement the following objects and methods:

First, you create a new MyEntityManagerClient class with an instance variable, serviceStub. serviceStub represents a gRPC client-side service that provides methods for your application to interact with the Lattice API.

MyEntityManagerClient.java
public class MyEntityManagerClient {

private EntityManagerAPIStub serviceStub;

public MyEntityManagerClient(){
// Creates a channel object to connect to Lattice using your environment's URL
ManagedChannel channel = ManagedChannelBuilder.forAddress(${YOUR_LATTICE_URL}, 443).useTransportSecurity().build();

Metadata header = new Metadata();
Metadata.Key<String> key = Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER);
header.put(key, "Bearer ${YOUR_BEARER_TOKEN}");

// Creates an interceptor to apply authorization headers to requests.
ClientInterceptor interceptor = MetadataUtils.newAttachHeadersInterceptor(header);

this.serviceStub = EntityManagerAPIGrpc.newStub(channel).withInterceptors(interceptor);
}

Then, you define a getEntity class method that wraps the service stub request with a StreamObserver object. The StreamObserver interface is part of the Java Stream API and is used to process a stream of values as they are emitted by a publisher.

The method also uses CountdownLatch, a tool that enables your application to wait and process asynchronous data:

MyEntityManagerClient.java
public void getEntity(String entityId) throws InterruptedException {
// Create a CountDownLatch with an initial count of 1.
// This latch will be used to wait for the response to be received before returning.
final CountDownLatch finishLatch = new CountDownLatch(1);
StreamObserver<GetEntityResponse> responseObserver = new StreamObserver<GetEntityResponse>() {
@Override
public void onNext(GetEntityResponse value) {
System.out.println(value);
// Count down the latch, indicating that a response has been received.
finishLatch.countDown();
}
@Override
public void onError(Throwable t) {
t.printStackTrace();
// Count down the latch, indicating that an error has occurred.
finishLatch.countDown();
}
@Override
public void onCompleted() {
System.out.println("Finished receiving data from server");
// Count down the latch, indicating that the response stream has been closed.
finishLatch.countDown();
}
};

// Create a request to retrieve the entity with the given ID.
GetEntityRequest request = GetEntityRequest.newBuilder().setEntityId(entityId).build();

// Send the request to the service stub and pass in the response observer to handle the response.
this.serviceStub.getEntity(request, responseObserver);

// Wait for the response to be received before returning.
finishLatch.await();
}

Finally, in App.java, you initiate a GetEntity request in the main thread to Lattice through the Entity Manager API. Lattice then fetches the entity from the common operational picture (COP):

App.java
   EntityManagerClient entityManager = new MyEntityManagerClient();
try {
// If you have existing entities in your environment,
// replace ENTITY_ID with the ID of any entity to fetch its details
entityManager.getEntity("$ENTITY_ID");
} catch (InterruptedException e) {
e.printStackTrace();
}

Allow self-signed certificates

warning

Make sure you understand the implications of using self-signed certificates. This should only be used for testing.

In order to connect to a server that is using a self-signed certificate, replace the ManagedChannel object in MyEntityManagerClient.java as shown in the following:

// Add the following import statement to the top of the file. 
+ import io.grpc.Channel;
.
.
.
// Remove the following statement.
- ManagedChannel channel = ManagedChannelBuilder.forAddress("$YOUR_LATTICE_URL", 443).useTransportSecurity().build();
+
// Replace with the following.
+ Channel channel;
+ try {
+ // Create a trust manager that skips all verification checks.
+ AdvancedTlsX509TrustManager clientTrustManager = AdvancedTlsX509TrustManager.newBuilder().setVerification(Verification.INSECURELY_SKIP_ALL_VERIFICATION).build();
+
+ // Create channel credentials using the trust manager.
+ ChannelCredentials channelCredentials = TlsChannelCredentials.newBuilder().trustManager(clientTrustManager).build();
+
+ // Establish a connection to Lattice using your Lattice environment URL and credentials.
+ channel = Grpc.newChannelBuilderForAddress("$YOUR_LATTICE_URL", 443, channelCredentials).build();
+ } catch (Exception e) {
+
+ // Log a helpful error message if there is an exception while generating the certificate.
+ Logger.getGlobal().log(Level.INFO, "Failed to generate Certificate- %s", e);
+ return;
+ }

What's next