Java SDK
The official Java SDK for Rexec provides an enterprise-ready interface for Terminal as a Service.Requirements
- Java 17 or later
- Maven or Gradle
Installation
<dependency>
<groupId>io.pipeops</groupId>
<artifactId>rexec</artifactId>
<version>1.0.0</version>
</dependency>
Quick Start
import io.pipeops.rexec.*;
public class Example {
public static void main(String[] args) throws RexecException {
// Create client
RexecClient client = new RexecClient(
"https://your-instance.com",
"your-api-token"
);
// Create a container
Container container = client.containers().create("ubuntu:24.04");
System.out.println("Created: " + container.getId());
// Start it
client.containers().start(container.getId());
// Execute a command
ExecResult result = client.containers().exec(
container.getId(),
"echo 'Hello from Java!'"
);
System.out.println(result.getStdout());
// Clean up
client.containers().delete(container.getId());
}
}
Client Initialization
Basic Client
import io.pipeops.rexec.RexecClient;
RexecClient client = new RexecClient(
"https://your-instance.com",
"your-api-token"
);
With Custom Timeout
import io.pipeops.rexec.RexecClient;
import io.pipeops.rexec.RexecConfig;
RexecConfig config = RexecConfig.builder()
.baseUrl("https://your-instance.com")
.token("your-api-token")
.timeout(60) // seconds
.build();
RexecClient client = new RexecClient(config);
Try-with-Resources
try (RexecClient client = new RexecClient(baseUrl, token)) {
// Use client - automatically closed
}
Container Operations
The Container service provides methods for managing sandboxed environments.List Containers
List<Container> containers = client.containers().list();
for (Container c : containers) {
System.out.println(c.getName() + ": " + c.getStatus());
}
Get Container
Container container = client.containers().get(containerId);
System.out.println("Container " + container.getName() + " is " + container.getStatus());
Create Container
// Simple creation
Container container = client.containers().create("ubuntu:24.04");
// With options using builder pattern
Container container = client.containers().create(
new CreateContainerRequest("python:3.12")
.setName("my-python-sandbox")
.addEnv("PYTHONPATH", "/app")
.addEnv("DEBUG", "true")
.addLabel("project", "demo")
);
System.out.println("Created: " + container.getId());
Start Container
client.containers().start(containerId);
Stop Container
client.containers().stop(containerId);
Delete Container
client.containers().delete(containerId);
Execute Commands
ExecResult result = client.containers().exec(
containerId,
"python --version"
);
if (result.isSuccess()) {
System.out.println("Output: " + result.getStdout());
} else {
System.err.println("Error: " + result.getStderr());
}
// Execute with array command
ExecResult result = client.containers().exec(
containerId,
new String[]{"python", "-c", "print('Hello')"}
);
File Operations
Manage files and directories within containers.List Files
FileService files = client.files();
List<FileInfo> entries = files.list(containerId, "/app");
for (FileInfo file : entries) {
String type = file.isDirectory() ?
"DIR" : file.getSize() + " bytes";
System.out.println(file.getName() + " - " + type);
}
Read File
String content = files.readString(containerId, "/etc/hostname");
System.out.println("Hostname: " + content);
// Or read as bytes
byte[] bytes = files.read(containerId, "/path/to/file");
Write File
files.write(
containerId,
"/app/script.py",
"print('Hello!')"
);
// Or write bytes
files.write(containerId, "/path/to/file", byteArray);
Delete File
files.delete(containerId, "/tmp/scratch.txt");
Interactive Terminal
Connect to containers via WebSocket for real-time terminal access.Basic Terminal Usage
Terminal terminal = client.terminal().connect(containerId);
// Set up handlers
terminal.onData(data -> System.out.print(data))
.onClose(() -> System.out.println("Disconnected"))
.onError(e -> e.printStackTrace());
// Send commands
terminal.write("ls -la\n");
terminal.write("cd /app && python main.py\n");
// Resize terminal
terminal.resize(120, 40);
// Clean up
terminal.close();
Terminal with Custom Size
Terminal terminal = client.terminal().connect(
containerId,
120, // cols
40 // rows
);
Advanced Examples
Run Script and Capture Output
public String runScript(
RexecClient client,
String containerId,
String script
) throws RexecException {
StringBuilder output = new StringBuilder();
CountDownLatch latch = new CountDownLatch(1);
Terminal terminal = client.terminal().connect(containerId);
terminal.onData(data -> output.append(data))
.onClose(() -> latch.countDown());
terminal.write(script + "\nexit\n");
try {
latch.await(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return output.toString();
}
// Usage
String result = runScript(
client,
container.getId(),
"apt update && apt install -y curl"
);
System.out.println(result);
Parallel Container Creation
import java.util.concurrent.*;
import java.util.stream.*;
public List<Container> createBatch(
RexecClient client,
int count
) throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newFixedThreadPool(5);
List<CompletableFuture<Container>> futures = IntStream.range(0, count)
.mapToObj(i -> CompletableFuture.supplyAsync(() -> {
try {
return client.containers().create(
new CreateContainerRequest("ubuntu:24.04")
.setName("worker-" + i)
);
} catch (RexecException e) {
throw new RuntimeException(e);
}
}, executor))
.collect(Collectors.toList());
List<Container> containers = futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
executor.shutdown();
return containers;
}
// Create 5 containers in parallel
List<Container> containers = createBatch(client, 5);
System.out.println("Created " + containers.size() + " containers");
File Upload
import java.nio.file.*;
public void uploadFile(
RexecClient client,
String containerId,
Path localPath,
String remotePath
) throws IOException, RexecException {
byte[] content = Files.readAllBytes(localPath);
client.files().write(containerId, remotePath, content);
}
// Usage
uploadFile(
client,
container.getId(),
Paths.get("./local-script.sh"),
"/home/script.sh"
);
Directory Sync
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
public void syncDirectory(
RexecClient client,
String containerId,
Path localDir,
String remoteDir
) throws IOException, RexecException {
client.files().mkdir(containerId, remoteDir);
Files.walkFileTree(localDir, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
try {
Path relativePath = localDir.relativize(file);
String remotePath = remoteDir + "/" +
relativePath.toString().replace("\\", "/");
byte[] content = Files.readAllBytes(file);
client.files().write(containerId, remotePath, content);
System.out.println("Uploaded: " + remotePath);
} catch (RexecException e) {
throw new IOException(e);
}
return FileVisitResult.CONTINUE;
}
});
}
Real-time Log Streaming
public void streamLogs(
RexecClient client,
String containerId,
String command
) throws RexecException {
Terminal terminal = client.terminal().connect(containerId);
terminal.onData(data -> {
System.out.print(data);
// Could also save to file
// try (FileWriter fw = new FileWriter("logs.txt", true)) {
// fw.write(data);
// }
});
terminal.onClose(() -> System.out.println("\nStream ended"));
terminal.write(command + "\n");
// Keep connection open
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// Usage
streamLogs(client, container.getId(), "tail -f /var/log/app.log");
Error Handling
import io.pipeops.rexec.RexecException;
try {
Container container = client.containers().get("invalid-id");
} catch (RexecException e) {
if (e.isApiError()) {
System.err.println("API error " + e.getStatusCode() +
": " + e.getMessage());
} else {
System.err.println("Network error: " + e.getMessage());
}
}
Stream API Integration
Use Java Streams with SDK results:import java.util.stream.Collectors;
// Filter containers
List<Container> running = client.containers().list()
.stream()
.filter(c -> "running".equals(c.getStatus()))
.collect(Collectors.toList());
// Count by image
Map<String, Long> countByImage = client.containers().list()
.stream()
.collect(Collectors.groupingBy(
Container::getImage,
Collectors.counting()
));
// Get container names
List<String> names = client.containers().list()
.stream()
.map(Container::getName)
.sorted()
.collect(Collectors.toList());
Builder Pattern
The SDK uses builder patterns for complex configurations:CreateContainerRequest request = new CreateContainerRequest("ubuntu:24.04")
.setName("my-container")
.addEnv("VAR1", "value1")
.addEnv("VAR2", "value2")
.addLabel("team", "backend")
.addLabel("env", "production");
Container container = client.containers().create(request);
Thread Safety
The Java SDK is thread-safe for concurrent operations:import java.util.concurrent.*;
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
final int index = i;
executor.submit(() -> {
try {
Container container = client.containers().create(
new CreateContainerRequest("ubuntu:24.04")
.setName("worker-" + index)
);
System.out.println("Created: " + container.getId());
} catch (RexecException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);