Java Protocol Buffer classes for Google's common protos, providing type-safe access to core Google Cloud API structures and gRPC service definitions
—
Support for asynchronous operations that don't complete immediately, providing status tracking, cancellation, and result retrieval. This is essential for operations that may take minutes or hours to complete.
Represents a long-running operation with status tracking and result handling.
class Operation {
String getName(); // Unique operation identifier
Any getMetadata(); // Operation-specific metadata
boolean getDone(); // True if operation is complete
Status getError(); // Error status if operation failed
Any getResponse(); // Operation result if successful
static Operation.Builder newBuilder();
Operation.Builder toBuilder();
static Operation parseFrom(byte[] data);
boolean hasError();
boolean hasResponse();
}
interface OperationOrBuilder {
String getName();
Any getMetadata();
boolean getDone();
boolean hasError();
Status getError();
boolean hasResponse();
Any getResponse();
}Metadata about operation types and their expected response/metadata types.
class OperationInfo {
String getResponseType(); // Fully qualified type name of response
String getMetadataType(); // Fully qualified type name of metadata
static OperationInfo.Builder newBuilder();
}Request to retrieve the current status of an operation.
class GetOperationRequest {
String getName(); // Operation name to retrieve
static GetOperationRequest.Builder newBuilder();
}Request to list operations matching specified criteria.
class ListOperationsRequest {
String getName(); // Collection name (e.g., "operations")
String getFilter(); // Optional filter expression
int getPageSize(); // Maximum operations to return
String getPageToken(); // Token for pagination
static ListOperationsRequest.Builder newBuilder();
}Response containing a list of operations and pagination information.
class ListOperationsResponse {
repeated Operation getOperationsList();
String getNextPageToken();
static ListOperationsResponse.Builder newBuilder();
int getOperationsCount();
Operation getOperations(int index);
}Request to cancel a running operation.
class CancelOperationRequest {
String getName(); // Operation name to cancel
static CancelOperationRequest.Builder newBuilder();
}Request to delete an operation record.
class DeleteOperationRequest {
String getName(); // Operation name to delete
static DeleteOperationRequest.Builder newBuilder();
}Request to wait for an operation to complete.
class WaitOperationRequest {
String getName();
Duration getTimeout(); // Maximum time to wait
static WaitOperationRequest.Builder newBuilder();
}import com.google.longrunning.Operation;
import com.google.longrunning.GetOperationRequest;
import com.google.protobuf.Any;
import com.google.rpc.Status;
// Create a new operation
Operation operation = Operation.newBuilder()
.setName("operations/my-operation-123")
.setDone(false)
.build();
// Check operation status
public void checkOperationStatus(String operationName) {
GetOperationRequest request = GetOperationRequest.newBuilder()
.setName(operationName)
.build();
// This would typically be sent to the service
Operation operation = operationsClient.getOperation(request);
if (operation.getDone()) {
if (operation.hasError()) {
Status error = operation.getError();
System.err.println("Operation failed: " + error.getMessage());
} else if (operation.hasResponse()) {
Any response = operation.getResponse();
System.out.println("Operation completed successfully");
// Process response based on expected type
}
} else {
System.out.println("Operation still in progress: " + operation.getName());
}
}import java.util.concurrent.TimeUnit;
public class OperationPoller {
private static final int MAX_POLL_ATTEMPTS = 100;
private static final long POLL_INTERVAL_SECONDS = 5;
public Operation waitForOperation(String operationName) throws InterruptedException {
for (int attempt = 0; attempt < MAX_POLL_ATTEMPTS; attempt++) {
GetOperationRequest request = GetOperationRequest.newBuilder()
.setName(operationName)
.build();
Operation operation = operationsClient.getOperation(request);
if (operation.getDone()) {
return operation;
}
System.out.println("Operation " + operationName + " still running, attempt " + (attempt + 1));
TimeUnit.SECONDS.sleep(POLL_INTERVAL_SECONDS);
}
throw new RuntimeException("Operation did not complete within timeout");
}
}import com.google.longrunning.ListOperationsRequest;
import com.google.longrunning.ListOperationsResponse;
public void listAllOperations(String collectionName) {
String pageToken = "";
do {
ListOperationsRequest request = ListOperationsRequest.newBuilder()
.setName(collectionName)
.setPageSize(50)
.setPageToken(pageToken)
.build();
ListOperationsResponse response = operationsClient.listOperations(request);
for (Operation operation : response.getOperationsList()) {
System.out.println("Operation: " + operation.getName() +
", Done: " + operation.getDone());
}
pageToken = response.getNextPageToken();
} while (!pageToken.isEmpty());
}public void listPendingOperations() {
ListOperationsRequest request = ListOperationsRequest.newBuilder()
.setName("operations")
.setFilter("done = false") // Only show incomplete operations
.setPageSize(100)
.build();
ListOperationsResponse response = operationsClient.listOperations(request);
System.out.println("Pending operations:");
for (Operation operation : response.getOperationsList()) {
System.out.println("- " + operation.getName());
}
}import com.google.longrunning.CancelOperationRequest;
public void cancelOperation(String operationName) {
CancelOperationRequest request = CancelOperationRequest.newBuilder()
.setName(operationName)
.build();
try {
operationsClient.cancelOperation(request);
System.out.println("Cancellation requested for operation: " + operationName);
// Verify cancellation
Operation operation = operationsClient.getOperation(
GetOperationRequest.newBuilder().setName(operationName).build());
if (operation.getDone() && operation.hasError()) {
Status error = operation.getError();
if (error.getCode() == Code.CANCELLED.getNumber()) {
System.out.println("Operation successfully cancelled");
}
}
} catch (Exception e) {
System.err.println("Failed to cancel operation: " + e.getMessage());
}
}import com.google.protobuf.InvalidProtocolBufferException;
// Example response type
class ProcessingResult {
private String resultId;
private int itemsProcessed;
// ... other fields
}
public ProcessingResult handleTypedOperation(Operation operation)
throws InvalidProtocolBufferException {
if (!operation.getDone()) {
throw new IllegalStateException("Operation not complete");
}
if (operation.hasError()) {
Status error = operation.getError();
throw new RuntimeException("Operation failed: " + error.getMessage());
}
if (!operation.hasResponse()) {
throw new IllegalStateException("Operation completed but no response");
}
// Unpack the Any response to the expected type
Any response = operation.getResponse();
if (response.is(ProcessingResult.class)) {
return response.unpack(ProcessingResult.class);
} else {
throw new IllegalArgumentException("Unexpected response type: " + response.getTypeUrl());
}
}// Example metadata type
class ProcessingMetadata {
private int totalItems;
private int processedItems;
private String currentPhase;
// ... other fields
}
public void displayProgress(Operation operation) throws InvalidProtocolBufferException {
if (operation.hasMetadata()) {
Any metadata = operation.getMetadata();
if (metadata.is(ProcessingMetadata.class)) {
ProcessingMetadata progress = metadata.unpack(ProcessingMetadata.class);
double percentComplete = (double) progress.getProcessedItems() / progress.getTotalItems() * 100;
System.out.printf("Operation %s: %.1f%% complete (%s)\n",
operation.getName(),
percentComplete,
progress.getCurrentPhase());
}
}
}import com.google.longrunning.DeleteOperationRequest;
public void cleanupCompletedOperations() {
// First, list completed operations
ListOperationsRequest listRequest = ListOperationsRequest.newBuilder()
.setName("operations")
.setFilter("done = true")
.build();
ListOperationsResponse response = operationsClient.listOperations(listRequest);
// Delete operations older than a certain threshold
long cutoffTime = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7);
for (Operation operation : response.getOperationsList()) {
// This example assumes operation names contain timestamps
// In practice, you'd use metadata or other fields to determine age
if (shouldDeleteOperation(operation, cutoffTime)) {
DeleteOperationRequest deleteRequest = DeleteOperationRequest.newBuilder()
.setName(operation.getName())
.build();
try {
operationsClient.deleteOperation(deleteRequest);
System.out.println("Deleted operation: " + operation.getName());
} catch (Exception e) {
System.err.println("Failed to delete operation " + operation.getName() + ": " + e.getMessage());
}
}
}
}
private boolean shouldDeleteOperation(Operation operation, long cutoffTime) {
// Implementation depends on how you track operation timestamps
// This is just an example
return true; // Replace with actual logic
}Operations should have unique, descriptive names:
// Good: includes service, resource type, and unique ID
String operationName = "operations/compute/instances/create-instance-abc123";
// Good: hierarchical naming for organization
String operationName = "projects/my-project/operations/backup-database-456789";Always check for errors before processing results:
public void handleOperation(Operation operation) {
if (!operation.getDone()) {
// Operation still in progress
return;
}
if (operation.hasError()) {
// Handle error case
Status error = operation.getError();
handleOperationError(error);
return;
}
if (operation.hasResponse()) {
// Process successful result
processOperationResult(operation.getResponse());
}
}Implement appropriate timeout strategies for different operation types:
public class OperationConfig {
// Short operations: file uploads, simple transformations
public static final Duration SHORT_OPERATION_TIMEOUT = Duration.newBuilder()
.setSeconds(300) // 5 minutes
.build();
// Medium operations: data processing, image generation
public static final Duration MEDIUM_OPERATION_TIMEOUT = Duration.newBuilder()
.setSeconds(1800) // 30 minutes
.build();
// Long operations: large data imports, model training
public static final Duration LONG_OPERATION_TIMEOUT = Duration.newBuilder()
.setSeconds(7200) // 2 hours
.build();
}Always clean up completed operations to avoid resource leaks:
public void performOperationWithCleanup(String operationName) {
try {
Operation result = waitForOperation(operationName);
processOperationResult(result);
} finally {
// Clean up the operation record
try {
DeleteOperationRequest deleteRequest = DeleteOperationRequest.newBuilder()
.setName(operationName)
.build();
operationsClient.deleteOperation(deleteRequest);
} catch (Exception e) {
System.err.println("Warning: Failed to clean up operation " + operationName);
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-com-google-api-grpc--proto-google-common-protos