High-level API that greatly simplifies using ZooKeeper.
—
Optional schema validation system for enforcing data structure and path constraints on ZooKeeper nodes, providing data integrity and consistency guarantees in distributed applications.
Collection and management of schemas for comprehensive validation across ZooKeeper paths.
/**
* Return this instance's schema set
* @return schema set
*/
SchemaSet getSchemaSet();
/**
* Collection of schemas for validation
*/
public class SchemaSet {
/**
* Get the default schema set (allows everything)
* @return default schema set
*/
public static SchemaSet getDefaultSchemaSet();
/**
* Create a new schema set builder
* @return builder
*/
public static Builder builder();
/**
* Get schema for a specific path
* @param path the path to check
* @return matching schema or null
*/
public Schema getSchema(String path);
/**
* Get schema by name
* @param name schema name
* @return named schema or null
*/
public Schema getNamedSchema(String name);
/**
* Get all schemas in this set
* @return collection of schemas
*/
public Collection<Schema> getSchemas();
/**
* Generate documentation for this schema set
* @return documentation string
*/
public String toDocumentation();
/**
* Check if schemas are enforced
* @return true if enforced
*/
public boolean isEnforced();
/**
* Builder for creating schema sets
*/
public static class Builder {
/**
* Add a schema to the set
* @param schema schema to add
* @return this builder
*/
public Builder schema(Schema schema);
/**
* Set whether schemas should be enforced
* @param enforced true to enforce
* @return this builder
*/
public Builder enforced(boolean enforced);
/**
* Build the schema set
* @return schema set
*/
public SchemaSet build();
}
}Usage Examples:
// Create custom schema set
SchemaSet customSchemas = SchemaSet.builder()
.schema(Schema.builder()
.name("UserConfig")
.path("/config/users")
.dataValidator(new JsonSchemaValidator())
.build())
.schema(Schema.builder()
.name("ServiceRegistry")
.pathRegex("/services/[^/]+")
.dataValidator(new ServiceConfigValidator())
.build())
.enforced(true)
.build();
// Create client with schema validation
CuratorFramework clientWithSchemas = CuratorFrameworkFactory.builder()
.connectString("localhost:2181")
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.schemaSet(customSchemas)
.build();
// Get current schema set
SchemaSet currentSchemas = client.getSchemaSet();
System.out.println("Schema enforcement: " + currentSchemas.isEnforced());
// List all schemas
for (Schema schema : currentSchemas.getSchemas()) {
System.out.println("Schema: " + schema.getName() + " -> " + schema.getPath());
}
// Generate documentation
String docs = currentSchemas.toDocumentation();
System.out.println("Schema Documentation:\n" + docs);Individual schema definitions for specific paths or path patterns with custom validation rules.
/**
* Defines validation rules for ZNode paths
*/
public class Schema {
/**
* Create a new schema builder
* @return builder
*/
public static SchemaBuilder builder();
/**
* Get the schema name
* @return name
*/
public String getName();
/**
* Get the exact path (if specified)
* @return exact path or null
*/
public String getPath();
/**
* Get the path regex pattern (if specified)
* @return regex pattern or null
*/
public Pattern getPathRegex();
/**
* Get the schema validator
* @return validator
*/
public SchemaValidator getSchemaValidator();
/**
* Get the documentation for this schema
* @return documentation string
*/
public String getDocumentation();
/**
* Generate documentation for this schema
* @return formatted documentation
*/
public String toDocumentation();
/**
* Check if this schema matches the given path
* @param path path to check
* @return true if matches
*/
public boolean matches(String path);
}
/**
* Builder for creating Schema instances
*/
public class SchemaBuilder {
/**
* Set the schema name
* @param name schema name
* @return this builder
*/
public SchemaBuilder name(String name);
/**
* Set an exact path for this schema
* @param path exact path
* @return this builder
*/
public SchemaBuilder path(String path);
/**
* Set a regex pattern for matching paths
* @param pathRegex regex pattern
* @return this builder
*/
public SchemaBuilder pathRegex(String pathRegex);
/**
* Set the data validator
* @param validator schema validator
* @return this builder
*/
public SchemaBuilder dataValidator(SchemaValidator validator);
/**
* Set documentation for this schema
* @param documentation documentation string
* @return this builder
*/
public SchemaBuilder documentation(String documentation);
/**
* Build the schema
* @return schema instance
*/
public Schema build();
}Usage Examples:
// Exact path schema
Schema configSchema = Schema.builder()
.name("ApplicationConfig")
.path("/app/config")
.dataValidator(new JsonSchemaValidator("{\"type\": \"object\"}"))
.documentation("Application configuration in JSON format")
.build();
// Regex path schema for service registry
Schema serviceSchema = Schema.builder()
.name("ServiceEndpoints")
.pathRegex("/services/[a-zA-Z0-9-]+/endpoints")
.dataValidator(new ServiceEndpointValidator())
.documentation("Service endpoint configurations")
.build();
// Schema for ephemeral nodes
Schema ephemeralSchema = Schema.builder()
.name("Sessions")
.pathRegex("/sessions/.*")
.dataValidator(new SessionDataValidator())
.documentation("User session data (ephemeral nodes)")
.build();
// Check path matching
System.out.println("Matches config: " + configSchema.matches("/app/config"));
System.out.println("Matches service: " + serviceSchema.matches("/services/user-service/endpoints"));
// Get schema details
System.out.println("Schema name: " + configSchema.getName());
System.out.println("Schema path: " + configSchema.getPath());
System.out.println("Schema docs: " + configSchema.getDocumentation());Validator interfaces and implementations for enforcing data constraints and structure validation.
/**
* Validates ZNode data
*/
public interface SchemaValidator {
/**
* Validate the data for a given path and stat
* @param path the path
* @param data the data to validate
* @param stat the stat information
* @return true if valid
* @throws SchemaViolation if validation fails
*/
boolean isValid(String path, byte[] data, Stat stat) throws SchemaViolation;
}
/**
* Default validator that allows everything
*/
public class DefaultSchemaValidator implements SchemaValidator {
@Override
public boolean isValid(String path, byte[] data, Stat stat) {
return true; // Always valid
}
}
/**
* Exception for schema validation violations
*/
public class SchemaViolation extends IllegalArgumentException {
/**
* Create schema violation with message
* @param message violation message
*/
public SchemaViolation(String message);
/**
* Create schema violation with message and cause
* @param message violation message
* @param cause underlying cause
*/
public SchemaViolation(String message, Throwable cause);
/**
* Get the path that caused the violation
* @return path
*/
public String getPath();
/**
* Get the schema that was violated
* @return schema
*/
public Schema getSchema();
}Custom Validator Examples:
// JSON Schema Validator
public class JsonSchemaValidator implements SchemaValidator {
private final String jsonSchema;
public JsonSchemaValidator(String jsonSchema) {
this.jsonSchema = jsonSchema;
}
@Override
public boolean isValid(String path, byte[] data, Stat stat) throws SchemaViolation {
if (data == null || data.length == 0) {
throw new SchemaViolation("JSON data cannot be empty for path: " + path);
}
try {
String jsonData = new String(data, StandardCharsets.UTF_8);
ObjectMapper mapper = new ObjectMapper();
mapper.readTree(jsonData); // Parse to validate JSON
// Here you would validate against the JSON schema
// Using a library like json-schema-validator
return true;
} catch (Exception e) {
throw new SchemaViolation("Invalid JSON data for path: " + path, e);
}
}
}
// Size-based Validator
public class SizeValidator implements SchemaValidator {
private final int maxSize;
private final int minSize;
public SizeValidator(int minSize, int maxSize) {
this.minSize = minSize;
this.maxSize = maxSize;
}
@Override
public boolean isValid(String path, byte[] data, Stat stat) throws SchemaViolation {
int size = data != null ? data.length : 0;
if (size < minSize) {
throw new SchemaViolation(
String.format("Data size %d is below minimum %d for path: %s", size, minSize, path)
);
}
if (size > maxSize) {
throw new SchemaViolation(
String.format("Data size %d exceeds maximum %d for path: %s", size, maxSize, path)
);
}
return true;
}
}
// Service Configuration Validator
public class ServiceConfigValidator implements SchemaValidator {
@Override
public boolean isValid(String path, byte[] data, Stat stat) throws SchemaViolation {
if (data == null || data.length == 0) {
throw new SchemaViolation("Service configuration cannot be empty: " + path);
}
try {
String config = new String(data, StandardCharsets.UTF_8);
Properties props = new Properties();
props.load(new StringReader(config));
// Validate required properties
if (!props.containsKey("service.name")) {
throw new SchemaViolation("Missing required property 'service.name' in: " + path);
}
if (!props.containsKey("service.port")) {
throw new SchemaViolation("Missing required property 'service.port' in: " + path);
}
// Validate port range
String portStr = props.getProperty("service.port");
int port = Integer.parseInt(portStr);
if (port < 1024 || port > 65535) {
throw new SchemaViolation("Invalid port range for: " + path);
}
return true;
} catch (Exception e) {
throw new SchemaViolation("Invalid service configuration for: " + path, e);
}
}
}
// Usage examples
Schema jsonConfigSchema = Schema.builder()
.name("JsonConfig")
.pathRegex("/config/.*\\.json")
.dataValidator(new JsonSchemaValidator("{\"type\": \"object\"}"))
.build();
Schema sizeConstrainedSchema = Schema.builder()
.name("SmallData")
.pathRegex("/cache/.*")
.dataValidator(new SizeValidator(1, 1024)) // 1 byte to 1KB
.build();
Schema serviceConfigSchema = Schema.builder()
.name("ServiceConfig")
.pathRegex("/services/.*/config")
.dataValidator(new ServiceConfigValidator())
.build();Utility classes for loading schemas from external configuration sources.
/**
* Utility for loading schema sets from configuration
*/
public class SchemaSetLoader {
/**
* Load schema set from JSON configuration
* @param jsonConfig JSON configuration string
* @return schema set
* @throws Exception if loading fails
*/
public static SchemaSet loadFromJson(String jsonConfig) throws Exception;
/**
* Load schema set from properties file
* @param properties properties configuration
* @return schema set
* @throws Exception if loading fails
*/
public static SchemaSet loadFromProperties(Properties properties) throws Exception;
/**
* Load schema set from resource file
* @param resourcePath path to resource file
* @return schema set
* @throws Exception if loading fails
*/
public static SchemaSet loadFromResource(String resourcePath) throws Exception;
}Configuration Examples:
// Load schemas from JSON configuration
String jsonConfig = """
{
"enforced": true,
"schemas": [
{
"name": "UserProfiles",
"path": "/users",
"validator": "json",
"config": {"type": "object", "required": ["id", "name"]}
},
{
"name": "ServiceRegistry",
"pathRegex": "/services/[^/]+",
"validator": "size",
"config": {"minSize": 10, "maxSize": 1024}
}
]
}
""";
SchemaSet loadedSchemas = SchemaSetLoader.loadFromJson(jsonConfig);
// Load schemas from properties
Properties props = new Properties();
props.setProperty("schemas.enforced", "true");
props.setProperty("schemas.user.name", "UserData");
props.setProperty("schemas.user.path", "/users/*");
props.setProperty("schemas.user.validator", "json");
SchemaSet propsSchemas = SchemaSetLoader.loadFromProperties(props);
// Load from resource file
SchemaSet resourceSchemas = SchemaSetLoader.loadFromResource("/schemas/zk-schemas.json");
// Apply loaded schemas to client
CuratorFramework schemaClient = CuratorFrameworkFactory.builder()
.connectString("localhost:2181")
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.schemaSet(loadedSchemas)
.build();How schema validation is applied during ZooKeeper operations and error handling strategies.
Usage Examples:
// Schema violations during operations
try {
// This will trigger schema validation
client.create()
.forPath("/config/app.json", "invalid json data".getBytes());
} catch (SchemaViolation e) {
System.err.println("Schema violation: " + e.getMessage());
System.err.println("Path: " + e.getPath());
System.err.println("Schema: " + e.getSchema().getName());
// Handle validation error appropriately
// Maybe try with corrected data or use default values
}
// Check schema before operation
SchemaSet schemas = client.getSchemaSet();
Schema pathSchema = schemas.getSchema("/config/database.json");
if (pathSchema != null) {
try {
// Validate data before sending
String jsonData = "{\"host\": \"localhost\", \"port\": 5432}";
pathSchema.getSchemaValidator().isValid("/config/database.json",
jsonData.getBytes(), null);
// If validation passes, proceed with operation
client.create()
.forPath("/config/database.json", jsonData.getBytes());
} catch (SchemaViolation e) {
System.err.println("Pre-validation failed: " + e.getMessage());
// Handle validation failure
}
}
// Disable schema enforcement temporarily
SchemaSet permissiveSchemas = SchemaSet.builder()
.enforced(false) // Validation warnings only, no exceptions
.build();
CuratorFramework permissiveClient = client.usingNamespace("temp")
.// Note: Can't change schema set on existing client
// Would need new client instanceInstall with Tessl CLI
npx tessl i tessl/maven-org-apache-curator--curator-framework