CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-cdap-cdap--cdap-client

CDAP Java Client library providing programmatic APIs for interacting with the CDAP platform

Pending
Overview
Eval results
Files

service-management.mddocs/

Service Management

The ServiceClient provides comprehensive user service interactions including endpoint discovery, availability checking, routing configuration, and direct service method calls. Services are HTTP-based programs that expose RESTful endpoints.

ServiceClient

public class ServiceClient {
    // Constructors
    public ServiceClient(ClientConfig config);
    public ServiceClient(ClientConfig config, RESTClient restClient);
    
    // Service information methods
    public ServiceSpecification get(ProgramId service);
    public List<ServiceHttpEndpoint> getEndpoints(ServiceId service);
    public void checkAvailability(ServiceId service);
    public URL getVersionedServiceURL(ServiceId service);
    public URL getServiceURL(ServiceId service);
    
    // Route configuration methods
    public Map<String, Integer> getRouteConfig(ServiceId serviceId);
    public void storeRouteConfig(ServiceId serviceId, Map<String, Integer> routeConfig);
    public void deleteRouteConfig(ServiceId serviceId);
    
    // Service call methods
    public HttpResponse callServiceMethod(ServiceId serviceId, String methodPath);
}

Service Types and Information

public class ServiceSpecification {
    public String getName();
    public String getClassName();
    public String getDescription();
    public Map<String, HttpServiceHandlerSpecification> getHandlers();
    public ResourceSpecification getResources();
}

public class ServiceHttpEndpoint {
    public String getMethod();
    public String getPath();
}

public class ServiceId {
    public static ServiceId of(ApplicationId application, String service);
    public ApplicationId getApplication();
    public String getService();
}

public class HttpResponse {
    public int getResponseCode();
    public Map<String, List<String>> getHeaders();
    public String getResponseMessage();
    public byte[] getResponseBody();
    public String getResponseBodyAsString();
}

Service Discovery and Information

Service Specification

// Get service specification
ApplicationId appId = ApplicationId.of(namespace, "web-analytics", "1.0.0");
ProgramId serviceProgram = ProgramId.of(appId, ProgramType.SERVICE, "analytics-service");
ServiceSpecification spec = serviceClient.get(serviceProgram);

System.out.println("Service: " + spec.getName());
System.out.println("Class: " + spec.getClassName());
System.out.println("Description: " + spec.getDescription());

// Get handlers information
Map<String, HttpServiceHandlerSpecification> handlers = spec.getHandlers();
for (Map.Entry<String, HttpServiceHandlerSpecification> entry : handlers.entrySet()) {
    System.out.println("Handler: " + entry.getKey());
    HttpServiceHandlerSpecification handler = entry.getValue();
    System.out.println("  Class: " + handler.getClassName());
    System.out.println("  Endpoints: " + handler.getEndpoints().size());
}

// Get resource requirements
ResourceSpecification resources = spec.getResources();
System.out.println("Memory: " + resources.getMemoryMB() + " MB");
System.out.println("VCores: " + resources.getVirtualCores());

Endpoint Discovery

// Get all endpoints for a service
ServiceId serviceId = ServiceId.of(appId, "analytics-service");
List<ServiceHttpEndpoint> endpoints = serviceClient.getEndpoints(serviceId);

System.out.println("Service endpoints:");
for (ServiceHttpEndpoint endpoint : endpoints) {
    System.out.println("  " + endpoint.getMethod() + " " + endpoint.getPath());
}

// Common endpoint patterns
for (ServiceHttpEndpoint endpoint : endpoints) {
    String method = endpoint.getMethod();
    String path = endpoint.getPath();
    
    if ("GET".equals(method) && path.contains("/status")) {
        System.out.println("Health check endpoint: " + method + " " + path);
    } else if ("POST".equals(method)) {
        System.out.println("Data submission endpoint: " + method + " " + path);
    } else if ("GET".equals(method) && path.contains("/metrics")) {
        System.out.println("Metrics endpoint: " + method + " " + path);
    }
}

Service Availability

// Check if service is available
try {
    serviceClient.checkAvailability(serviceId);
    System.out.println("Service is available: " + serviceId.getService());
} catch (ServiceUnavailableException e) {
    System.err.println("Service unavailable: " + serviceId.getService());
} catch (ServiceNotFoundException e) {
    System.err.println("Service not found: " + serviceId.getService());
}

// Wait for service availability
public void waitForServiceAvailability(ServiceId serviceId, int maxAttempts, long delayMs) {
    for (int i = 0; i < maxAttempts; i++) {
        try {
            serviceClient.checkAvailability(serviceId);
            System.out.println("Service is now available: " + serviceId.getService());
            return;
        } catch (ServiceUnavailableException e) {
            if (i == maxAttempts - 1) {
                throw new RuntimeException("Service did not become available after " + maxAttempts + " attempts");
            }
            System.out.println("Service not available, attempt " + (i + 1) + "/" + maxAttempts);
            try {
                Thread.sleep(delayMs);
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("Interrupted while waiting for service", ie);
            }
        }
    }
}

Service URL Management

URL Generation

// Get service URLs
URL versionedUrl = serviceClient.getVersionedServiceURL(serviceId);
URL unversionedUrl = serviceClient.getServiceURL(serviceId);

System.out.println("Versioned URL: " + versionedUrl);
System.out.println("Unversioned URL: " + unversionedUrl);

// Build endpoint URLs
String baseUrl = unversionedUrl.toString();
for (ServiceHttpEndpoint endpoint : endpoints) {
    String endpointUrl = baseUrl + endpoint.getPath();
    System.out.println(endpoint.getMethod() + " -> " + endpointUrl);
}

Dynamic URL Construction

// Build URLs for different environments
public class ServiceURLBuilder {
    private final ServiceClient serviceClient;
    
    public ServiceURLBuilder(ServiceClient serviceClient) {
        this.serviceClient = serviceClient;
    }
    
    public String buildEndpointURL(ServiceId serviceId, String path, boolean versioned) {
        try {
            URL baseUrl = versioned ? 
                serviceClient.getVersionedServiceURL(serviceId) :
                serviceClient.getServiceURL(serviceId);
            
            return baseUrl.toString() + (path.startsWith("/") ? path : "/" + path);
        } catch (Exception e) {
            throw new RuntimeException("Error building service URL", e);
        }
    }
    
    public String buildParameterizedURL(ServiceId serviceId, String pathTemplate, Object... params) {
        String path = String.format(pathTemplate, params);
        return buildEndpointURL(serviceId, path, false);
    }
}

// Usage
ServiceURLBuilder urlBuilder = new ServiceURLBuilder(serviceClient);
String userUrl = urlBuilder.buildParameterizedURL(serviceId, "/users/%s", "user123");
String metricsUrl = urlBuilder.buildEndpointURL(serviceId, "/metrics", false);

Route Configuration

Traffic Routing

// Get current route configuration
Map<String, Integer> currentRoutes = serviceClient.getRouteConfig(serviceId);
System.out.println("Current routes: " + currentRoutes);

// Configure traffic routing between service versions
Map<String, Integer> routeConfig = Map.of(
    "v1.0.0", 80,  // 80% traffic to v1.0.0
    "v1.1.0", 20   // 20% traffic to v1.1.0 (canary deployment)
);

serviceClient.storeRouteConfig(serviceId, routeConfig);
System.out.println("Updated route configuration for gradual rollout");

// Blue-green deployment routing
Map<String, Integer> blueGreenRoutes = Map.of(
    "blue", 0,    // Old version (no traffic)
    "green", 100  // New version (all traffic)
);
serviceClient.storeRouteConfig(serviceId, blueGreenRoutes);

Advanced Routing Strategies

// Gradual traffic migration
public void performGradualMigration(ServiceId serviceId, String oldVersion, String newVersion) {
    int[] trafficSplits = {90, 80, 60, 40, 20, 0}; // Gradual reduction of old version traffic
    
    for (int oldTraffic : trafficSplits) {
        int newTraffic = 100 - oldTraffic;
        
        Map<String, Integer> routes = Map.of(
            oldVersion, oldTraffic,
            newVersion, newTraffic
        );
        
        try {
            serviceClient.storeRouteConfig(serviceId, routes);
            System.out.println("Route config: " + oldVersion + "=" + oldTraffic + "%, " + 
                             newVersion + "=" + newTraffic + "%");
            
            // Wait and monitor before next step
            Thread.sleep(300000); // 5 minutes
            
            // Check service health before continuing
            serviceClient.checkAvailability(serviceId);
            
        } catch (Exception e) {
            System.err.println("Error during migration step: " + e.getMessage());
            // Rollback to previous configuration
            Map<String, Integer> rollbackRoutes = Map.of(oldVersion, 100);
            serviceClient.storeRouteConfig(serviceId, rollbackRoutes);
            throw new RuntimeException("Migration failed, rolled back", e);
        }
    }
    
    System.out.println("Migration completed successfully");
}

// Remove route configuration (default routing)
serviceClient.deleteRouteConfig(serviceId);
System.out.println("Route configuration removed, using default routing");

Service Method Calls

Direct Service Calls

// Make direct calls to service methods
try {
    // GET request
    HttpResponse response = serviceClient.callServiceMethod(serviceId, "/status");
    System.out.println("Status response: " + response.getResponseCode());
    System.out.println("Body: " + response.getResponseBodyAsString());
    
    // Check response headers
    Map<String, List<String>> headers = response.getHeaders();
    if (headers.containsKey("Content-Type")) {
        System.out.println("Content-Type: " + headers.get("Content-Type").get(0));
    }
    
} catch (ServiceUnavailableException e) {
    System.err.println("Service call failed - service unavailable");
} catch (IOException e) {
    System.err.println("Network error during service call: " + e.getMessage());
}

Advanced HTTP Client Usage

// Service interaction with custom HTTP client
public class ServiceInteractor {
    private final ServiceClient serviceClient;
    private final ServiceId serviceId;
    
    public ServiceInteractor(ServiceClient serviceClient, ServiceId serviceId) {
        this.serviceClient = serviceClient;
        this.serviceId = serviceId;
    }
    
    public String getServiceStatus() {
        try {
            HttpResponse response = serviceClient.callServiceMethod(serviceId, "/status");
            if (response.getResponseCode() == 200) {
                return response.getResponseBodyAsString();
            } else {
                throw new RuntimeException("Service status check failed: " + response.getResponseCode());
            }
        } catch (Exception e) {
            throw new RuntimeException("Error checking service status", e);
        }
    }
    
    public Map<String, Object> getServiceMetrics() {
        try {
            HttpResponse response = serviceClient.callServiceMethod(serviceId, "/metrics");
            if (response.getResponseCode() == 200) {
                String jsonResponse = response.getResponseBodyAsString();
                // Parse JSON response (using your preferred JSON library)
                return parseJsonResponse(jsonResponse);
            } else {
                throw new RuntimeException("Metrics retrieval failed: " + response.getResponseCode());
            }
        } catch (Exception e) {
            throw new RuntimeException("Error retrieving service metrics", e);
        }
    }
    
    public boolean isHealthy() {
        try {
            serviceClient.checkAvailability(serviceId);
            HttpResponse healthResponse = serviceClient.callServiceMethod(serviceId, "/health");
            return healthResponse.getResponseCode() == 200;
        } catch (Exception e) {
            return false;
        }
    }
    
    private Map<String, Object> parseJsonResponse(String json) {
        // Implement JSON parsing using your preferred library (Jackson, Gson, etc.)
        // This is a placeholder implementation
        return Map.of("status", "parsed");
    }
}

Service Health Monitoring

Health Check Implementation

// Comprehensive service health monitoring
public class ServiceHealthMonitor {
    private final ServiceClient serviceClient;
    private final ServiceId serviceId;
    
    public ServiceHealthMonitor(ServiceClient serviceClient, ServiceId serviceId) {
        this.serviceClient = serviceClient;
        this.serviceId = serviceId;
    }
    
    public ServiceHealthStatus checkHealth() {
        ServiceHealthStatus.Builder statusBuilder = ServiceHealthStatus.builder()
            .serviceId(serviceId)
            .timestamp(System.currentTimeMillis());
        
        try {
            // Check basic availability
            serviceClient.checkAvailability(serviceId);
            statusBuilder.available(true);
            
            // Check endpoints
            List<ServiceHttpEndpoint> endpoints = serviceClient.getEndpoints(serviceId);
            statusBuilder.endpointCount(endpoints.size());
            
            // Test health endpoint if available
            for (ServiceHttpEndpoint endpoint : endpoints) {
                if ("GET".equals(endpoint.getMethod()) && 
                    (endpoint.getPath().contains("/health") || endpoint.getPath().contains("/status"))) {
                    
                    HttpResponse response = serviceClient.callServiceMethod(serviceId, endpoint.getPath());
                    statusBuilder.healthEndpointStatus(response.getResponseCode());
                    statusBuilder.healthEndpointResponse(response.getResponseBodyAsString());
                    break;
                }
            }
            
            // Check route configuration
            Map<String, Integer> routes = serviceClient.getRouteConfig(serviceId);
            statusBuilder.routeConfiguration(routes);
            
            statusBuilder.healthy(true);
            
        } catch (ServiceNotFoundException e) {
            statusBuilder.available(false).healthy(false).error("Service not found");
        } catch (ServiceUnavailableException e) {
            statusBuilder.available(false).healthy(false).error("Service unavailable");
        } catch (Exception e) {
            statusBuilder.available(false).healthy(false).error("Error: " + e.getMessage());
        }
        
        return statusBuilder.build();
    }
    
    public void monitorContinuously(long intervalMs, HealthStatusCallback callback) {
        Thread monitorThread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    ServiceHealthStatus status = checkHealth();
                    callback.onHealthStatus(status);
                    Thread.sleep(intervalMs);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                } catch (Exception e) {
                    callback.onError(e);
                }
            }
        });
        
        monitorThread.setDaemon(true);
        monitorThread.start();
    }
    
    @FunctionalInterface
    public interface HealthStatusCallback {
        void onHealthStatus(ServiceHealthStatus status);
        
        default void onError(Exception e) {
            System.err.println("Health monitoring error: " + e.getMessage());
        }
    }
}

// Health status data class
public class ServiceHealthStatus {
    private final ServiceId serviceId;
    private final long timestamp;
    private final boolean available;
    private final boolean healthy;
    private final int endpointCount;
    private final Integer healthEndpointStatus;
    private final String healthEndpointResponse;
    private final Map<String, Integer> routeConfiguration;
    private final String error;
    
    // Constructor, getters, and builder implementation
    public static Builder builder() {
        return new Builder();
    }
    
    public static class Builder {
        // Builder implementation
    }
}

Error Handling

Service management operations may throw these exceptions:

  • ServiceNotFoundException: Service does not exist
  • ServiceUnavailableException: Service is not currently available
  • ServiceNotRunningException: Service exists but is not running
  • RouteConfigurationException: Invalid route configuration
  • UnauthenticatedException: Authentication required
  • UnauthorizedException: Insufficient permissions
try {
    ServiceSpecification spec = serviceClient.get(serviceProgram);
    System.out.println("Service specification retrieved");
} catch (ServiceNotFoundException e) {
    System.err.println("Service not found: " + serviceId.getService());
} catch (UnauthorizedException e) {
    System.err.println("No permission to access service: " + e.getMessage());
} catch (IOException e) {
    System.err.println("Network error: " + e.getMessage());
}

Best Practices

  1. Health Monitoring: Implement continuous health monitoring for services
  2. Route Management: Use route configuration for gradual deployments
  3. Error Handling: Implement proper retry logic and circuit breakers
  4. URL Management: Use versioned URLs for backward compatibility
  5. Performance: Monitor service response times and throughput
  6. Security: Ensure proper authentication and authorization for service calls
// Good: Comprehensive service management with error handling and monitoring
public class ServiceManager {
    private final ServiceClient serviceClient;
    private final ServiceId serviceId;
    private final ServiceHealthMonitor healthMonitor;
    
    public ServiceManager(ServiceClient serviceClient, ServiceId serviceId) {
        this.serviceClient = serviceClient;
        this.serviceId = serviceId;
        this.healthMonitor = new ServiceHealthMonitor(serviceClient, serviceId);
    }
    
    public void deployWithHealthCheck(Map<String, Integer> routeConfig) {
        try {
            // Store new route configuration
            serviceClient.storeRouteConfig(serviceId, routeConfig);
            
            // Wait for deployment to stabilize
            Thread.sleep(30000); // 30 seconds
            
            // Verify service health
            ServiceHealthStatus status = healthMonitor.checkHealth();
            if (!status.isHealthy()) {
                // Rollback on failure
                System.err.println("Service unhealthy after deployment, rolling back");
                serviceClient.deleteRouteConfig(serviceId); // Use default routing
                throw new RuntimeException("Deployment failed health check");
            }
            
            System.out.println("Deployment successful and service is healthy");
            
        } catch (Exception e) {
            System.err.println("Deployment failed: " + e.getMessage());
            throw new RuntimeException("Service deployment failed", e);
        }
    }
    
    public String callServiceSafely(String methodPath, int maxRetries) {
        Exception lastException = null;
        
        for (int i = 0; i < maxRetries; i++) {
            try {
                serviceClient.checkAvailability(serviceId);
                HttpResponse response = serviceClient.callServiceMethod(serviceId, methodPath);
                
                if (response.getResponseCode() == 200) {
                    return response.getResponseBodyAsString();
                } else if (response.getResponseCode() >= 500) {
                    // Server error - retry
                    Thread.sleep(1000 * (i + 1)); // Exponential backoff
                    continue;
                } else {
                    // Client error - don't retry
                    throw new RuntimeException("Service call failed: " + response.getResponseCode());
                }
                
            } catch (Exception e) {
                lastException = e;
                if (i < maxRetries - 1) {
                    System.out.println("Service call failed, retrying... (" + (i + 1) + "/" + maxRetries + ")");
                    try {
                        Thread.sleep(1000 * (i + 1));
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        throw new RuntimeException("Interrupted during retry", ie);
                    }
                }
            }
        }
        
        throw new RuntimeException("Service call failed after " + maxRetries + " attempts", lastException);
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-io-cdap-cdap--cdap-client

docs

application-management.md

artifact-management.md

configuration.md

data-operations.md

dataset-operations.md

index.md

metrics-monitoring.md

program-control.md

schedule-management.md

security-administration.md

service-management.md

tile.json