Compute Module Java SDK
Java SDK는 ComputeModule builder로 함수를 등록하고 실행합니다. Java 21 이상을 사용합니다.
| 클래스 | 용도 |
|---|---|
com.palantir.computemodules.ComputeModule | 모듈 구성과 시작 |
com.palantir.computemodules.functions.Context | 함수 호출의 작업 ID |
com.palantir.computemodules.auth.PipelinesAuth | Pipeline 모드의 빌드 토큰 조회 |
com.palantir.computemodules.auth.RefreshingOauthToken | 애플리케이션 토큰 조회 및 갱신 |
Builder와 함수 서명
import com.palantir.computemodules.ComputeModule;
import com.palantir.computemodules.functions.Context;
public final class App {
public record Payload(String name) {}
public static void main(String[] args) {
ComputeModule.builder()
.add(App::hello, Payload.class, String.class, "hello")
.add(App::nameLength, Payload.class, Integer.class, "nameLength")
.build()
.start();
}
static String hello(Context context, Payload payload) {
return "Hello, " + payload.name();
}
static Integer nameLength(Context context, Payload payload) {
return payload.name().length();
}
}
add()의 인자는 함수 참조, 입력 클래스, 출력 클래스, 엔드포인트 이름 순서입니다. 함수의 첫 인자는 Context, 두 번째 인자는 입력 payload여야 합니다. 입력 클래스는 두 번째 인자의 타입과, 출력 클래스는 반환 타입과 일치해야 합니다.
Context는 jobId()를 제공합니다. Application permissions의 client ID와 secret은 환경 변수로 읽습니다. Foundry 함수로 사용할 입력은 명명된 필드를 가진 복합 타입으로 구성합니다.
입력과 반환 타입
Java record를 사용할 수 있습니다. 각 public record는 자체 파일에 두거나 위 예제처럼 public 클래스 안에 선언합니다.
public record Car(String brand, int doors) {}
Jackson 클래스를 사용한다면 JSON 속성 이름과 인자 없는 생성자가 필요합니다.
import com.fasterxml.jackson.annotation.JsonProperty;
public final class Car {
@JsonProperty("brand")
private String brand;
@JsonProperty("doors")
private int doors;
public Car() {}
public String brand() { return brand; }
public int doors() { return doors; }
}
Pipeline 토큰
Pipeline 모드에서는 선언한 입력과 출력 자원으로 범위가 제한된 빌드 토큰을 사용합니다.
import com.palantir.computemodules.auth.PipelinesAuth;
public final class PipelineToken {
public static String get() {
return PipelinesAuth.retrievePipelineToken();
}
}
토큰을 사용자 로그에 기록하지 않습니다. 선언하지 않은 자원에 접근할 권한을 이 토큰으로 얻지는 못합니다.
애플리케이션 토큰
Application permissions에서는 서비스 사용자의 CLIENT_ID와 CLIENT_SECRET이 제공됩니다. FOUNDRY_HOST는 프로토콜을 제외한 Foundry 호스트입니다.
import com.palantir.computemodules.auth.RefreshingOauthToken;
import java.util.List;
public final class DatasetTokens {
private static final RefreshingOauthToken TOKENS = new RefreshingOauthToken(
System.getenv("FOUNDRY_HOST"),
List.of("api:datasets-read", "api:datasets-write"));
public static String get() {
return TOKENS.getToken();
}
}
getToken()은 토큰이 없거나 기본 갱신 간격인 30분을 지났으면 다시 가져옵니다. 갱신 결과가 비어 있으면 예외가 발생합니다. 토큰을 처음 한 번만 꺼내 장시간 보관하지 말고 요청할 때 wrapper를 통해 가져옵니다.
OSDK 사용
Developer Console에서 Java OSDK를 생성하고 패키지를 의존성에 추가합니다. 서비스 사용자에게 대상 객체 접근 권한을 부여하며 Foundry 호스트로 나가는 소스를 연결해야 합니다.
아래 demo_sdk와 Employee는 가상 생성 패키지 및 객체 유형 이름입니다. 실제 SDK 이름으로 바꾸고 FOUNDRY_HOST를 설정합니다.
import com.palantir.foundry.demo_sdk.FoundryClient;
import com.palantir.foundry.demo_sdk.objects.Employee;
import com.palantir.osdk.api.Auth;
import com.palantir.osdk.api.auth.ConfidentialClientAuth;
import com.palantir.osdk.internal.api.FoundryConnectionConfig;
import java.util.Optional;
import java.util.stream.Stream;
public final class EmployeeLookup {
public static Optional<String> firstEmployee() {
Auth auth = ConfidentialClientAuth.builder()
.clientId(System.getenv("CLIENT_ID"))
.clientSecret(System.getenv("CLIENT_SECRET"))
.build();
FoundryClient client = FoundryClient.builder()
.auth(auth)
.connectionConfig(FoundryConnectionConfig.builder()
.foundryUri("https://" + System.getenv("FOUNDRY_HOST"))
.build())
.build();
try (Stream<Employee> employees = client.ontology().objects().Employee().fetchStream()) {
return employees.findFirst().map(Employee::toString);
}
}
}
결과가 없으면 Optional이 비어 있으며 조회 실패는 예외로 전달합니다. 스트림은 첫 객체를 찾은 뒤 닫습니다. CLIENT_ID와 CLIENT_SECRET은 EnvVars.Reserved.CLIENT_ID.get() 및 EnvVars.Reserved.CLIENT_SECRET.get()으로도 조회할 수 있습니다.
SLS 로그
start()는 STDOUT에 SLS service.1 JSON 로그를 출력하도록 설정하고 session_id, job_id와 pid를 포함합니다. SafeLogger를 사용하면 비민감 로그 매개변수와 민감 매개변수를 구분할 수 있습니다.
다음 계산 함수는 입력 값과 사용자 이름을 로그에 남기지 않습니다.
import com.palantir.computemodules.ComputeModule;
import com.palantir.computemodules.functions.Context;
import com.palantir.logsafe.logger.SafeLogger;
import com.palantir.logsafe.logger.SafeLoggerFactory;
public final class Calculator {
private static final SafeLogger LOG = SafeLoggerFactory.get(Calculator.class);
public record CalculateInput(double x, double y, String operation) {}
public record CalculateOutput(double result, String operation) {}
public static void main(String[] args) {
ComputeModule.builder()
.add(Calculator::calculate, CalculateInput.class,
CalculateOutput.class, "calculate")
.build()
.start();
}
static CalculateOutput calculate(Context context, CalculateInput input) {
LOG.debug("Starting calculation");
try {
double result = switch (input.operation()) {
case "add" -> input.x() + input.y();
case "subtract" -> input.x() - input.y();
case "multiply" -> input.x() * input.y();
case "divide" -> {
if (input.y() == 0) {
throw new IllegalArgumentException("The divisor must not be zero");
}
yield input.x() / input.y();
}
default -> throw new IllegalArgumentException("Unsupported operation");
};
LOG.info("Calculation completed");
return new CalculateOutput(result, input.operation());
} catch (RuntimeException error) {
LOG.error("Calculation failed", error);
throw error;
}
}
}
매개변수를 기록할 때 SafeArg는 비민감 데이터에, UnsafeArg는 민감할 수 있는 데이터에 사용합니다. 이름이나 입력 값이라는 이유만으로 SafeArg로 지정하지 않습니다. 로그 확인은 복제본 진단을 따릅니다.
이전 DeployedAppRuntime 사용 시
이전 SDK의 주 클래스가 ComputeModule 대신 DeployedAppRuntime이면 Configure에서 Include runtime을 OFF로 설정해야 합니다. 이 조건을 새 ComputeModule의 설정과 혼동하지 않습니다.