Skip to content

Commit 7194f77

Browse files
Changes for adding collector context as a ThreadLocal object
1 parent a67b7bd commit 7194f77

File tree

7 files changed

+293
-42
lines changed

7 files changed

+293
-42
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ For the latest version, please check the [release](https://github.com/networknt/
110110

111111
## [Customized MetaSchema](doc/cust-meta.md)
112112

113+
## [Collector Context](doc/collector_context.md)
114+
113115

114116
## Known issues
115117

doc/collector_context.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
### CollectorContext
2+
3+
4+
There could be usecases where we want collect the information while we are validating the data. A simple example could be fetching some value from a database or from a microservice based on the data (which could be a text or a JSON object) in a given JSON node and the schema keyword we are using.
5+
6+
The fetched data can be stored some where so that it can be used later after the validation is done. Since the current validation logic already parses the data and schema, both validation and collecting the required information can be done in one go.
7+
8+
CollectorContext and Collector classes are designed to satisfy this usage.
9+
10+
#### How to use CollectorContext
11+
12+
Objects of CollectorContext live on ThreadLocal which is unique for every thread. This allows users to add objects to context at many points in the framework like Formats,Keywords,Validators etc.
13+
14+
CollectorContext instance can be obtained by calling the getInstance static method on CollectorContext.This method gives an instance from the ThreadLocal for the current thread.
15+
16+
Collectors are added to CollectorContext. Collectors allows to collect the objects. A Collector is added to CollectorContext with a name and corresponding Collector instance.
17+
18+
```
19+
CollectorContext collectorContext = CollectorContext.getInstance();
20+
collectorContext.add(SAMPLE_COLLECTOR_TYPE, new Collector<List<String>>() {
21+
@Override
22+
public List<String> collect() {
23+
List<String> references = new ArrayList<String>();
24+
references.add(getDatasourceMap().get(node.textValue()));
25+
return references;
26+
}
27+
});
28+
```
29+
30+
To validate the schema with the ability to use CollectorContext, validateAndCollect method has to be invoked on the JsonSchema class. This class returns a ValidationResult that contains the errors encountered during validation and a CollectorContext instance. Objects constructed by Collectors can be retrieved from CollectorContext by using the name they were added with.
31+
32+
33+
```
34+
ValidationResult validationResult = jsonSchema.validateAndCollect(jsonNode);
35+
CollectorContext context = validationResult.getCollectorContext();
36+
List<String> contextValue = (List<String>)context.get(SAMPLE_COLLECTOR_TYPE);
37+
38+
```
39+
40+
Note that CollectorContext will be removed from ThreadLocal once validateAndCollect method returns. Also the data is loaded into CollectorContext only after all the validations are done.
41+

src/main/java/com/networknt/schema/CollectorContext.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,27 @@
55
import java.util.Map.Entry;
66

77
/**
8-
* A Single Context for holding the output returned by the {@link Collector} implementations.
8+
* Context for holding the output returned by the {@link Collector}
9+
* implementations.
910
*
1011
*/
11-
public enum CollectorContext {
12+
public class CollectorContext {
1213

13-
INSTANCE;
14+
static final String COLLECTOR_CONTEXT_THREAD_LOCAL_KEY = "COLLECTOR_CONTEXT_THREAD_LOCAL_KEY";
15+
16+
// Get an instance from thread info (which uses ThreadLocal).
17+
public static CollectorContext getInstance() {
18+
return (CollectorContext) ThreadInfo.get(COLLECTOR_CONTEXT_THREAD_LOCAL_KEY);
19+
}
1420

1521
/**
1622
* Map for holding the collector type and {@link Collector}
1723
*/
1824
private Map<String, Collector<?>> collectorMap = new HashMap<String, Collector<?>>();
1925

2026
/**
21-
* Map for holding the collector type and {@link Collector} class collect method output.
27+
* Map for holding the collector type and {@link Collector} class collect method
28+
* output.
2229
*/
2330
private Map<String, Object> collectorLoadMap = new HashMap<String, Object>();
2431

src/main/java/com/networknt/schema/JsonSchema.java

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -177,25 +177,34 @@ public Set<ValidationMessage> validate(JsonNode jsonNode, JsonNode rootNode, Str
177177
return errors;
178178
}
179179

180-
public Set<ValidationMessage> validateAndCollect(JsonNode node) {
180+
public ValidationResult validateAndCollect(JsonNode node) {
181181
return validateAndCollect(node, node, AT_ROOT);
182182
}
183183

184184

185185
/**
186186
*
187-
* This method both validates and collects the data in a CollectionContext
187+
* This method both validates and collects the data in a CollectionContext.
188188
* @param jsonNode
189189
* @param rootNode
190190
* @param at
191191
* @return
192192
*/
193-
protected Set<ValidationMessage> validateAndCollect(JsonNode jsonNode, JsonNode rootNode, String at) {
194-
CollectorContext collectorContext = CollectorContext.INSTANCE;
195-
collectorContext.reset();
196-
Set<ValidationMessage> errors = validate(jsonNode, rootNode, at);
197-
collectorContext.load();
198-
return errors;
193+
protected ValidationResult validateAndCollect(JsonNode jsonNode, JsonNode rootNode, String at) {
194+
try {
195+
// Create the collector context object.
196+
CollectorContext collectorContext = new CollectorContext();
197+
// Set the collector context in thread info, this is unique for every thread.
198+
ThreadInfo.set(CollectorContext.COLLECTOR_CONTEXT_THREAD_LOCAL_KEY, collectorContext);
199+
Set<ValidationMessage> errors = validate(jsonNode, rootNode, at);
200+
// Load all the data from collectors into the context.
201+
collectorContext.load();
202+
// Collect errors and collector context into validation result.
203+
ValidationResult validationResult = new ValidationResult(errors, collectorContext);
204+
return validationResult;
205+
} finally {
206+
ThreadInfo.remove(CollectorContext.COLLECTOR_CONTEXT_THREAD_LOCAL_KEY);
207+
}
199208
}
200209

201210
@Override
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.networknt.schema;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
public class ThreadInfo {
7+
8+
private static ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal<Map<String, Object>>() {
9+
protected java.util.Map<String, Object> initialValue() {
10+
return new HashMap<String, Object>();
11+
};
12+
};
13+
14+
public static Object get(String key) {
15+
return threadLocal.get().get(key);
16+
}
17+
18+
public static void set(String key, Object value) {
19+
Map<String, Object> threadLocalMap = threadLocal.get();
20+
threadLocalMap.put(key, value);
21+
}
22+
23+
public static void remove(String key) {
24+
Map<String, Object> threadLocalMap = threadLocal.get();
25+
threadLocalMap.remove(key);
26+
}
27+
28+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.networknt.schema;
2+
3+
import java.util.Set;
4+
5+
public class ValidationResult {
6+
7+
private Set<ValidationMessage> validationMessages;
8+
9+
private CollectorContext collectorContext;
10+
11+
public ValidationResult(Set<ValidationMessage> validationMessages, CollectorContext collectorContext) {
12+
super();
13+
this.validationMessages = validationMessages;
14+
this.collectorContext = collectorContext;
15+
}
16+
17+
public Set<ValidationMessage> getValidationMessages() {
18+
return validationMessages;
19+
}
20+
21+
public CollectorContext getCollectorContext() {
22+
return collectorContext;
23+
}
24+
25+
}

0 commit comments

Comments
 (0)