Skip to content

Commit 38b8234

Browse files
authored
Merge pull request #260 from prashanthjos/master
Changes for adding collector context
2 parents c4c1891 + 7194f77 commit 38b8234

File tree

8 files changed

+429
-6
lines changed

8 files changed

+429
-6
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+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.networknt.schema;
2+
3+
/**
4+
* Basic interface that allows the implementers to collect the information and
5+
* return it.
6+
*
7+
* @param <E>
8+
*/
9+
public interface Collector<E> {
10+
11+
public E collect();
12+
13+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.networknt.schema;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
import java.util.Map.Entry;
6+
7+
/**
8+
* Context for holding the output returned by the {@link Collector}
9+
* implementations.
10+
*
11+
*/
12+
public class CollectorContext {
13+
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+
}
20+
21+
/**
22+
* Map for holding the collector type and {@link Collector}
23+
*/
24+
private Map<String, Collector<?>> collectorMap = new HashMap<String, Collector<?>>();
25+
26+
/**
27+
* Map for holding the collector type and {@link Collector} class collect method
28+
* output.
29+
*/
30+
private Map<String, Object> collectorLoadMap = new HashMap<String, Object>();
31+
32+
public <E> void add(String collectorType, Collector<E> collector) {
33+
collectorMap.put(collectorType, collector);
34+
}
35+
36+
public Object get(String collectorType) {
37+
if (collectorLoadMap.get(collectorType) == null && collectorMap.get(collectorType) != null) {
38+
collectorLoadMap.put(collectorType, collectorMap.get(collectorType).collect());
39+
}
40+
return collectorLoadMap.get(collectorType);
41+
}
42+
43+
/**
44+
* Load all the collectors associated with the context.
45+
*/
46+
void load() {
47+
for (Entry<String, Collector<?>> collectorEntrySet : collectorMap.entrySet()) {
48+
collectorLoadMap.put(collectorEntrySet.getKey(), collectorEntrySet.getValue().collect());
49+
}
50+
}
51+
52+
/**
53+
* Reset the context
54+
*/
55+
void reset() {
56+
this.collectorMap = new HashMap<String, Collector<?>>();
57+
this.collectorLoadMap = new HashMap<String, Object>();
58+
}
59+
60+
}

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

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,14 +169,44 @@ private Map<String, JsonValidator> read(JsonNode schemaNode) {
169169
return validators;
170170
}
171171

172-
public Set<ValidationMessage> validate(JsonNode jsonNode, JsonNode rootNode, String at) {
173-
Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
174-
for (JsonValidator v : validators.values()) {
175-
errors.addAll(v.validate(jsonNode, rootNode, at));
176-
}
177-
return errors;
172+
public Set<ValidationMessage> validate(JsonNode jsonNode, JsonNode rootNode, String at) {
173+
Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
174+
for (JsonValidator v : validators.values()) {
175+
errors.addAll(v.validate(jsonNode, rootNode, at));
176+
}
177+
return errors;
178+
}
179+
180+
public ValidationResult validateAndCollect(JsonNode node) {
181+
return validateAndCollect(node, node, AT_ROOT);
178182
}
179183

184+
185+
/**
186+
*
187+
* This method both validates and collects the data in a CollectionContext.
188+
* @param jsonNode
189+
* @param rootNode
190+
* @param at
191+
* @return
192+
*/
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+
}
208+
}
209+
180210
@Override
181211
public String toString() {
182212
return "\"" + getSchemaPath() + "\" : " + getSchemaNode().toString();
@@ -189,4 +219,5 @@ public boolean hasRequiredValidator() {
189219
public JsonValidator getRequiredValidator() {
190220
return requiredValidator;
191221
}
222+
192223
}
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)