0
# Schema Validation
1
2
Optional schema validation system for enforcing data structure and path constraints on ZooKeeper nodes, providing data integrity and consistency guarantees in distributed applications.
3
4
## Capabilities
5
6
### SchemaSet Management
7
8
Collection and management of schemas for comprehensive validation across ZooKeeper paths.
9
10
```java { .api }
11
/**
12
* Return this instance's schema set
13
* @return schema set
14
*/
15
SchemaSet getSchemaSet();
16
17
/**
18
* Collection of schemas for validation
19
*/
20
public class SchemaSet {
21
/**
22
* Get the default schema set (allows everything)
23
* @return default schema set
24
*/
25
public static SchemaSet getDefaultSchemaSet();
26
27
/**
28
* Create a new schema set builder
29
* @return builder
30
*/
31
public static Builder builder();
32
33
/**
34
* Get schema for a specific path
35
* @param path the path to check
36
* @return matching schema or null
37
*/
38
public Schema getSchema(String path);
39
40
/**
41
* Get schema by name
42
* @param name schema name
43
* @return named schema or null
44
*/
45
public Schema getNamedSchema(String name);
46
47
/**
48
* Get all schemas in this set
49
* @return collection of schemas
50
*/
51
public Collection<Schema> getSchemas();
52
53
/**
54
* Generate documentation for this schema set
55
* @return documentation string
56
*/
57
public String toDocumentation();
58
59
/**
60
* Check if schemas are enforced
61
* @return true if enforced
62
*/
63
public boolean isEnforced();
64
65
/**
66
* Builder for creating schema sets
67
*/
68
public static class Builder {
69
/**
70
* Add a schema to the set
71
* @param schema schema to add
72
* @return this builder
73
*/
74
public Builder schema(Schema schema);
75
76
/**
77
* Set whether schemas should be enforced
78
* @param enforced true to enforce
79
* @return this builder
80
*/
81
public Builder enforced(boolean enforced);
82
83
/**
84
* Build the schema set
85
* @return schema set
86
*/
87
public SchemaSet build();
88
}
89
}
90
```
91
92
**Usage Examples:**
93
94
```java
95
// Create custom schema set
96
SchemaSet customSchemas = SchemaSet.builder()
97
.schema(Schema.builder()
98
.name("UserConfig")
99
.path("/config/users")
100
.dataValidator(new JsonSchemaValidator())
101
.build())
102
.schema(Schema.builder()
103
.name("ServiceRegistry")
104
.pathRegex("/services/[^/]+")
105
.dataValidator(new ServiceConfigValidator())
106
.build())
107
.enforced(true)
108
.build();
109
110
// Create client with schema validation
111
CuratorFramework clientWithSchemas = CuratorFrameworkFactory.builder()
112
.connectString("localhost:2181")
113
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
114
.schemaSet(customSchemas)
115
.build();
116
117
// Get current schema set
118
SchemaSet currentSchemas = client.getSchemaSet();
119
System.out.println("Schema enforcement: " + currentSchemas.isEnforced());
120
121
// List all schemas
122
for (Schema schema : currentSchemas.getSchemas()) {
123
System.out.println("Schema: " + schema.getName() + " -> " + schema.getPath());
124
}
125
126
// Generate documentation
127
String docs = currentSchemas.toDocumentation();
128
System.out.println("Schema Documentation:\n" + docs);
129
```
130
131
### Schema Definition
132
133
Individual schema definitions for specific paths or path patterns with custom validation rules.
134
135
```java { .api }
136
/**
137
* Defines validation rules for ZNode paths
138
*/
139
public class Schema {
140
/**
141
* Create a new schema builder
142
* @return builder
143
*/
144
public static SchemaBuilder builder();
145
146
/**
147
* Get the schema name
148
* @return name
149
*/
150
public String getName();
151
152
/**
153
* Get the exact path (if specified)
154
* @return exact path or null
155
*/
156
public String getPath();
157
158
/**
159
* Get the path regex pattern (if specified)
160
* @return regex pattern or null
161
*/
162
public Pattern getPathRegex();
163
164
/**
165
* Get the schema validator
166
* @return validator
167
*/
168
public SchemaValidator getSchemaValidator();
169
170
/**
171
* Get the documentation for this schema
172
* @return documentation string
173
*/
174
public String getDocumentation();
175
176
/**
177
* Generate documentation for this schema
178
* @return formatted documentation
179
*/
180
public String toDocumentation();
181
182
/**
183
* Check if this schema matches the given path
184
* @param path path to check
185
* @return true if matches
186
*/
187
public boolean matches(String path);
188
}
189
190
/**
191
* Builder for creating Schema instances
192
*/
193
public class SchemaBuilder {
194
/**
195
* Set the schema name
196
* @param name schema name
197
* @return this builder
198
*/
199
public SchemaBuilder name(String name);
200
201
/**
202
* Set an exact path for this schema
203
* @param path exact path
204
* @return this builder
205
*/
206
public SchemaBuilder path(String path);
207
208
/**
209
* Set a regex pattern for matching paths
210
* @param pathRegex regex pattern
211
* @return this builder
212
*/
213
public SchemaBuilder pathRegex(String pathRegex);
214
215
/**
216
* Set the data validator
217
* @param validator schema validator
218
* @return this builder
219
*/
220
public SchemaBuilder dataValidator(SchemaValidator validator);
221
222
/**
223
* Set documentation for this schema
224
* @param documentation documentation string
225
* @return this builder
226
*/
227
public SchemaBuilder documentation(String documentation);
228
229
/**
230
* Build the schema
231
* @return schema instance
232
*/
233
public Schema build();
234
}
235
```
236
237
**Usage Examples:**
238
239
```java
240
// Exact path schema
241
Schema configSchema = Schema.builder()
242
.name("ApplicationConfig")
243
.path("/app/config")
244
.dataValidator(new JsonSchemaValidator("{\"type\": \"object\"}"))
245
.documentation("Application configuration in JSON format")
246
.build();
247
248
// Regex path schema for service registry
249
Schema serviceSchema = Schema.builder()
250
.name("ServiceEndpoints")
251
.pathRegex("/services/[a-zA-Z0-9-]+/endpoints")
252
.dataValidator(new ServiceEndpointValidator())
253
.documentation("Service endpoint configurations")
254
.build();
255
256
// Schema for ephemeral nodes
257
Schema ephemeralSchema = Schema.builder()
258
.name("Sessions")
259
.pathRegex("/sessions/.*")
260
.dataValidator(new SessionDataValidator())
261
.documentation("User session data (ephemeral nodes)")
262
.build();
263
264
// Check path matching
265
System.out.println("Matches config: " + configSchema.matches("/app/config"));
266
System.out.println("Matches service: " + serviceSchema.matches("/services/user-service/endpoints"));
267
268
// Get schema details
269
System.out.println("Schema name: " + configSchema.getName());
270
System.out.println("Schema path: " + configSchema.getPath());
271
System.out.println("Schema docs: " + configSchema.getDocumentation());
272
```
273
274
### Schema Validation
275
276
Validator interfaces and implementations for enforcing data constraints and structure validation.
277
278
```java { .api }
279
/**
280
* Validates ZNode data
281
*/
282
public interface SchemaValidator {
283
/**
284
* Validate the data for a given path and stat
285
* @param path the path
286
* @param data the data to validate
287
* @param stat the stat information
288
* @return true if valid
289
* @throws SchemaViolation if validation fails
290
*/
291
boolean isValid(String path, byte[] data, Stat stat) throws SchemaViolation;
292
}
293
294
/**
295
* Default validator that allows everything
296
*/
297
public class DefaultSchemaValidator implements SchemaValidator {
298
@Override
299
public boolean isValid(String path, byte[] data, Stat stat) {
300
return true; // Always valid
301
}
302
}
303
304
/**
305
* Exception for schema validation violations
306
*/
307
public class SchemaViolation extends IllegalArgumentException {
308
/**
309
* Create schema violation with message
310
* @param message violation message
311
*/
312
public SchemaViolation(String message);
313
314
/**
315
* Create schema violation with message and cause
316
* @param message violation message
317
* @param cause underlying cause
318
*/
319
public SchemaViolation(String message, Throwable cause);
320
321
/**
322
* Get the path that caused the violation
323
* @return path
324
*/
325
public String getPath();
326
327
/**
328
* Get the schema that was violated
329
* @return schema
330
*/
331
public Schema getSchema();
332
}
333
```
334
335
**Custom Validator Examples:**
336
337
```java
338
// JSON Schema Validator
339
public class JsonSchemaValidator implements SchemaValidator {
340
private final String jsonSchema;
341
342
public JsonSchemaValidator(String jsonSchema) {
343
this.jsonSchema = jsonSchema;
344
}
345
346
@Override
347
public boolean isValid(String path, byte[] data, Stat stat) throws SchemaViolation {
348
if (data == null || data.length == 0) {
349
throw new SchemaViolation("JSON data cannot be empty for path: " + path);
350
}
351
352
try {
353
String jsonData = new String(data, StandardCharsets.UTF_8);
354
ObjectMapper mapper = new ObjectMapper();
355
mapper.readTree(jsonData); // Parse to validate JSON
356
357
// Here you would validate against the JSON schema
358
// Using a library like json-schema-validator
359
return true;
360
} catch (Exception e) {
361
throw new SchemaViolation("Invalid JSON data for path: " + path, e);
362
}
363
}
364
}
365
366
// Size-based Validator
367
public class SizeValidator implements SchemaValidator {
368
private final int maxSize;
369
private final int minSize;
370
371
public SizeValidator(int minSize, int maxSize) {
372
this.minSize = minSize;
373
this.maxSize = maxSize;
374
}
375
376
@Override
377
public boolean isValid(String path, byte[] data, Stat stat) throws SchemaViolation {
378
int size = data != null ? data.length : 0;
379
380
if (size < minSize) {
381
throw new SchemaViolation(
382
String.format("Data size %d is below minimum %d for path: %s", size, minSize, path)
383
);
384
}
385
386
if (size > maxSize) {
387
throw new SchemaViolation(
388
String.format("Data size %d exceeds maximum %d for path: %s", size, maxSize, path)
389
);
390
}
391
392
return true;
393
}
394
}
395
396
// Service Configuration Validator
397
public class ServiceConfigValidator implements SchemaValidator {
398
@Override
399
public boolean isValid(String path, byte[] data, Stat stat) throws SchemaViolation {
400
if (data == null || data.length == 0) {
401
throw new SchemaViolation("Service configuration cannot be empty: " + path);
402
}
403
404
try {
405
String config = new String(data, StandardCharsets.UTF_8);
406
Properties props = new Properties();
407
props.load(new StringReader(config));
408
409
// Validate required properties
410
if (!props.containsKey("service.name")) {
411
throw new SchemaViolation("Missing required property 'service.name' in: " + path);
412
}
413
414
if (!props.containsKey("service.port")) {
415
throw new SchemaViolation("Missing required property 'service.port' in: " + path);
416
}
417
418
// Validate port range
419
String portStr = props.getProperty("service.port");
420
int port = Integer.parseInt(portStr);
421
if (port < 1024 || port > 65535) {
422
throw new SchemaViolation("Invalid port range for: " + path);
423
}
424
425
return true;
426
} catch (Exception e) {
427
throw new SchemaViolation("Invalid service configuration for: " + path, e);
428
}
429
}
430
}
431
432
// Usage examples
433
Schema jsonConfigSchema = Schema.builder()
434
.name("JsonConfig")
435
.pathRegex("/config/.*\\.json")
436
.dataValidator(new JsonSchemaValidator("{\"type\": \"object\"}"))
437
.build();
438
439
Schema sizeConstrainedSchema = Schema.builder()
440
.name("SmallData")
441
.pathRegex("/cache/.*")
442
.dataValidator(new SizeValidator(1, 1024)) // 1 byte to 1KB
443
.build();
444
445
Schema serviceConfigSchema = Schema.builder()
446
.name("ServiceConfig")
447
.pathRegex("/services/.*/config")
448
.dataValidator(new ServiceConfigValidator())
449
.build();
450
```
451
452
### Schema Loading and Configuration
453
454
Utility classes for loading schemas from external configuration sources.
455
456
```java { .api }
457
/**
458
* Utility for loading schema sets from configuration
459
*/
460
public class SchemaSetLoader {
461
/**
462
* Load schema set from JSON configuration
463
* @param jsonConfig JSON configuration string
464
* @return schema set
465
* @throws Exception if loading fails
466
*/
467
public static SchemaSet loadFromJson(String jsonConfig) throws Exception;
468
469
/**
470
* Load schema set from properties file
471
* @param properties properties configuration
472
* @return schema set
473
* @throws Exception if loading fails
474
*/
475
public static SchemaSet loadFromProperties(Properties properties) throws Exception;
476
477
/**
478
* Load schema set from resource file
479
* @param resourcePath path to resource file
480
* @return schema set
481
* @throws Exception if loading fails
482
*/
483
public static SchemaSet loadFromResource(String resourcePath) throws Exception;
484
}
485
```
486
487
**Configuration Examples:**
488
489
```java
490
// Load schemas from JSON configuration
491
String jsonConfig = """
492
{
493
"enforced": true,
494
"schemas": [
495
{
496
"name": "UserProfiles",
497
"path": "/users",
498
"validator": "json",
499
"config": {"type": "object", "required": ["id", "name"]}
500
},
501
{
502
"name": "ServiceRegistry",
503
"pathRegex": "/services/[^/]+",
504
"validator": "size",
505
"config": {"minSize": 10, "maxSize": 1024}
506
}
507
]
508
}
509
""";
510
511
SchemaSet loadedSchemas = SchemaSetLoader.loadFromJson(jsonConfig);
512
513
// Load schemas from properties
514
Properties props = new Properties();
515
props.setProperty("schemas.enforced", "true");
516
props.setProperty("schemas.user.name", "UserData");
517
props.setProperty("schemas.user.path", "/users/*");
518
props.setProperty("schemas.user.validator", "json");
519
520
SchemaSet propsSchemas = SchemaSetLoader.loadFromProperties(props);
521
522
// Load from resource file
523
SchemaSet resourceSchemas = SchemaSetLoader.loadFromResource("/schemas/zk-schemas.json");
524
525
// Apply loaded schemas to client
526
CuratorFramework schemaClient = CuratorFrameworkFactory.builder()
527
.connectString("localhost:2181")
528
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
529
.schemaSet(loadedSchemas)
530
.build();
531
```
532
533
### Schema Enforcement and Error Handling
534
535
How schema validation is applied during ZooKeeper operations and error handling strategies.
536
537
**Usage Examples:**
538
539
```java
540
// Schema violations during operations
541
try {
542
// This will trigger schema validation
543
client.create()
544
.forPath("/config/app.json", "invalid json data".getBytes());
545
546
} catch (SchemaViolation e) {
547
System.err.println("Schema violation: " + e.getMessage());
548
System.err.println("Path: " + e.getPath());
549
System.err.println("Schema: " + e.getSchema().getName());
550
551
// Handle validation error appropriately
552
// Maybe try with corrected data or use default values
553
}
554
555
// Check schema before operation
556
SchemaSet schemas = client.getSchemaSet();
557
Schema pathSchema = schemas.getSchema("/config/database.json");
558
559
if (pathSchema != null) {
560
try {
561
// Validate data before sending
562
String jsonData = "{\"host\": \"localhost\", \"port\": 5432}";
563
pathSchema.getSchemaValidator().isValid("/config/database.json",
564
jsonData.getBytes(), null);
565
566
// If validation passes, proceed with operation
567
client.create()
568
.forPath("/config/database.json", jsonData.getBytes());
569
570
} catch (SchemaViolation e) {
571
System.err.println("Pre-validation failed: " + e.getMessage());
572
// Handle validation failure
573
}
574
}
575
576
// Disable schema enforcement temporarily
577
SchemaSet permissiveSchemas = SchemaSet.builder()
578
.enforced(false) // Validation warnings only, no exceptions
579
.build();
580
581
CuratorFramework permissiveClient = client.usingNamespace("temp")
582
.// Note: Can't change schema set on existing client
583
// Would need new client instance
584
```