Skip to main content

OPA Java SDK Usage

Installation

The Java SDK is published on Maven Central as com.styra/opa. The Maven Central page includes up-to-date instructions to add it as a dependency to your Java project, tailored for a variety of build systems including Maven and Gradle.

Code Examples

The following examples assume an OPA server at http://localhost:8181 equipped with the following Rego policy:

package authz
import rego.v1

default allow := false
allow if input.subject == "alice"

and this data:

{
"roles": {
"admin": ["read", "write"]
}
}

Simple Query

For a simple boolean response with input, use the SDK as follows:

String opaURL = "http://localhost:8181";
OPAClient opa = new OPAClient(opaURL);

java.util.Map<String,Object> input = java.util.Map.ofEntries(
entry("subject", "alice"),
entry("action", "read"),
);

boolean allowed = false;

try {
allowed = opa.check("authz/allow", input);
} catch (OPAException e ) {
System.out.println("exception while making request against OPA: " + e);
}

System.out.println("allowed: " + allowed);

Simple Query with Output

The .evaluate() method can be used instead of .check() for non-boolean output types:

String opaURL = "http://localhost:8181";
OPAClient opa = new OPAClient(opaURL);

java.util.Map<String,Object> input = java.util.Map.ofEntries(
entry("subject", "alice"),
entry("action", "read"));

java.util.Map<String, Object> out = null;

try {
out = opa.evaluate("roles", input);
} catch (OPAException e ) {
System.out.println("exception while making request against OPA: " + e);
}

System.out.println("content of data.roles: " + out);

Objects for Input And Output

In Java, it can be more natural to use objects as inputs and outputs to a policy, rather than java.util.Map. This is fully supported by OPA-Java, including objects with type parameters. Internally, OPA-Java uses Jackson to handle converting between the JSON OPA consumes and emits, and native Java types. Due to type erasure, an implementation detail of Java's generics, deserializing OPA outputs into most types of classes, including the one in this example require an additional type reference argument which tells Jackson what type of class to deserialize the output into.

tip

If you see an error such as:

Exception in thread "main" java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.example.CustomContainer (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; org.example.CustomContainer is in unnamed module of loader 'app')`

It is likely you forgot the type reference argument to evaluate().

This is a minimal representative example for how to "round-trip"'; the input is Java object with a type parameter and so is the output.

// import com.fasterxml.jackson.core.type.TypeReference;

String opaURL = "http://localhost:8181";
OPAClient opa = new OPAClient(opaURL);

CustomContainer<Integer> in = new CustomContainer<Integer>();
in.setName("meaning of life");
in.setValue(42);

CustomContainer<String> out = null;

try {
out = opa.evaluate("obj/stringify", in, new TypeReference<CustomContainer<String>>() {});
} catch (OPAException e ) {
System.out.println("exception while making request against OPA: " + e);
}

System.out.println("out.getName(): " + out.getName());
System.out.println("out.getValue(): " + out.getValue());

To run this example, the following needs to added to the OPA policy by placing it in policy/obj/obj.rego:

package obj

stringify := {
"value": sprintf("%v", [input.value]),
"name": sprintf("%s (stringified)", [input.name]),
}

Additionally, the definition of CustomContainer must be placed at app/src/main/java/org/example/CustomContainer.java:

package org.example;

public class CustomContainer<T> {
private T value;
private String name;

public void setValue(T newValue) {
this.value = newValue;
}

public T getValue() {
return this.value;
}

public void setName(String newName) {
this.name = newName;
}

public String getName() {
return this.name;
}
}

Batched Queries

import com.styra.opa.OPAClient;
import com.styra.opa.OPAException;
import com.styra.opa.OPAResult;
import java.util.Map;
import static java.util.Map.entry;

String opaURL = "http://localhost:8181";
OPAClient opa = new OPAClient(opaURL);

Map<String,Object> alice = Map.ofEntries(
entry("subject", "alice"),
entry("action", "read"),
entry("resource", "/finance/reports/fy2038_budget.csv")
);

Map<String,Object> bob = Map.ofEntries(
entry("subject", "bob"),
entry("action", "write"),
entry("resource", "/finance/reports/fy2038_budget.csv")
);

Map<String,Object> input = Map.ofEntries(
entry("alice", alice),
entry("bob", bob)
);

Map<String,OPAResult> responses;

try {
responses = opa.evaluateBatch("authz/allow", input);

for (var entry : responses.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue().get());
}

} catch (OPAException e ) {
System.out.println("exception while making request against OPA: " + e);
throw e;
}
Result
bob: false
alice: true