Skip to content

Commit fb48aa4

Browse files
author
Lionel Montrieux
committed
Keep schema validation classes thread safe
1 parent bdc0add commit fb48aa4

14 files changed

+76
-138
lines changed

core/src/main/java/org/everit/json/schema/ArraySchema.java

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -200,40 +200,43 @@ public boolean requiresArray() {
200200
return requiresArray;
201201
}
202202

203-
private void testItemCount(final JSONArray subject) {
203+
private void testItemCount(final JSONArray subject, List<ValidationException> validationExceptions) {
204204
int actualLength = subject.length();
205205
if (minItems != null && actualLength < minItems) {
206-
addValidationException(failure("expected minimum item count: " + minItems
206+
validationExceptions.add(
207+
failure("expected minimum item count: " + minItems
207208
+ ", found: " + actualLength, "minItems"));
208209
return;
209210
}
210211
if (maxItems != null && maxItems < actualLength) {
211-
addValidationException(failure("expected maximum item count: " + maxItems
212+
validationExceptions.add(
213+
failure("expected maximum item count: " + maxItems
212214
+ ", found: " + actualLength, "maxItems"));
213215
}
214216
}
215217

216-
private void testItems(final JSONArray subject) {
218+
private void testItems(final JSONArray subject, List<ValidationException> validationExceptions) {
217219
if (allItemSchema != null) {
218220
validateItemsAgainstSchema(IntStream.range(0, subject.length()),
219221
subject,
220222
allItemSchema,
221-
this::addValidationException);
223+
validationExceptions::add);
222224
} else if (itemSchemas != null) {
223225
if (!additionalItems && subject.length() > itemSchemas.size()) {
224-
addValidationException(failure(format("expected: [%d] array items, found: [%d]",
226+
validationExceptions.add(
227+
failure(format("expected: [%d] array items, found: [%d]",
225228
itemSchemas.size(), subject.length()), "items"));
226229
}
227230
int itemValidationUntil = Math.min(subject.length(), itemSchemas.size());
228231
validateItemsAgainstSchema(IntStream.range(0, itemValidationUntil),
229232
subject,
230233
itemSchemas::get,
231-
this::addValidationException);
234+
validationExceptions::add);
232235
if (schemaOfAdditionalItems != null) {
233236
validateItemsAgainstSchema(IntStream.range(itemValidationUntil, subject.length()),
234237
subject,
235238
schemaOfAdditionalItems,
236-
this::addValidationException);
239+
validationExceptions::add);
237240
}
238241
}
239242
}
@@ -255,7 +258,7 @@ private void validateItemsAgainstSchema(final IntStream indices, final JSONArray
255258
}
256259
}
257260

258-
private void testUniqueness(final JSONArray subject) {
261+
private void testUniqueness(final JSONArray subject, List<ValidationException> validationExceptions) {
259262
if (subject.length() == 0) {
260263
return;
261264
}
@@ -264,7 +267,7 @@ private void testUniqueness(final JSONArray subject) {
264267
Object item = subject.get(i);
265268
for (Object contained : uniqueItems) {
266269
if (ObjectComparator.deepEquals(contained, item)) {
267-
addValidationException(
270+
validationExceptions.add(
268271
failure("array items are not unique", "uniqueItems"));
269272
return;
270273
}
@@ -280,21 +283,21 @@ public void validate(final Object subject) {
280283
throw failure(JSONArray.class, subject);
281284
}
282285
} else {
283-
validationExceptions = null;
286+
List<ValidationException> validationExceptions = new ArrayList<>();
284287
JSONArray arrSubject = (JSONArray) subject;
285-
testItemCount(arrSubject);
288+
testItemCount(arrSubject, validationExceptions);
286289
if (uniqueItems) {
287-
testUniqueness(arrSubject);
290+
testUniqueness(arrSubject, validationExceptions);
291+
}
292+
testItems(arrSubject, validationExceptions);
293+
testContains(arrSubject, validationExceptions);
294+
if (null != validationExceptions) {
295+
ValidationException.throwFor(this, validationExceptions);
288296
}
289-
testItems(arrSubject);
290-
testContains(arrSubject);
291-
}
292-
if (null != validationExceptions) {
293-
ValidationException.throwFor(this, validationExceptions);
294297
}
295298
}
296299

297-
private void testContains(JSONArray arrSubject) {
300+
private void testContains(JSONArray arrSubject, List<ValidationException> validationExceptions) {
298301
if (containedItemSchema == null) {
299302
return;
300303
}
@@ -304,7 +307,8 @@ private void testContains(JSONArray arrSubject) {
304307
return;
305308
}
306309
}
307-
addValidationException(failure("expected at least one array item to match 'contains' schema", "contains"));
310+
validationExceptions.add(
311+
failure("expected at least one array item to match 'contains' schema", "contains"));
308312
}
309313

310314
@Override

core/src/main/java/org/everit/json/schema/ObjectSchema.java

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -273,14 +273,14 @@ public boolean requiresObject() {
273273
return requiresObject;
274274
}
275275

276-
private void testAdditionalProperties(final JSONObject subject) {
276+
private void testAdditionalProperties(final JSONObject subject, List<ValidationException> validationExceptions) {
277277
if (!additionalProperties) {
278278
List<String> additionalProperties = getAdditionalProperties(subject);
279279
if (null == additionalProperties || additionalProperties.isEmpty()) {
280280
return;
281281
}
282282
for (String additionalProperty: additionalProperties) {
283-
addValidationException(new ValidationException(this,
283+
validationExceptions.add(new ValidationException(this,
284284
format("extraneous key [%s] is not permitted", additionalProperty), "additionalProperties"));
285285
}
286286
return;
@@ -290,13 +290,13 @@ private void testAdditionalProperties(final JSONObject subject) {
290290
Object propVal = subject.get(propName);
291291
Optional<ValidationException> exception = ifFails(schemaOfAdditionalProperties, propVal);
292292
if (exception.isPresent()) {
293-
addValidationException(exception.get().prepend(propName, this));
293+
validationExceptions.add(exception.get().prepend(propName, this));
294294
}
295295
}
296296
}
297297
}
298298

299-
private void testPatternProperties(final JSONObject subject) {
299+
private void testPatternProperties(final JSONObject subject, List<ValidationException> validationExceptions) {
300300
String[] propNames = JSONObject.getNames(subject);
301301
if (propNames == null || propNames.length == 0) {
302302
return;
@@ -306,65 +306,69 @@ private void testPatternProperties(final JSONObject subject) {
306306
if (entry.getKey().matcher(propName).find()) {
307307
Optional<ValidationException> exception = ifFails(entry.getValue(), subject.get(propName));
308308
if (exception.isPresent()) {
309-
addValidationException(exception.get().prepend(propName));
309+
validationExceptions.add(exception.get().prepend(propName));
310310
}
311311
}
312312
}
313313
}
314314
}
315315

316-
private void testProperties(final JSONObject subject) {
316+
private void testProperties(final JSONObject subject, List<ValidationException> validationExceptions) {
317317
if (propertySchemas != null) {
318318
for (Entry<String, Schema> entry : propertySchemas.entrySet()) {
319319
String key = entry.getKey();
320320
if (subject.has(key)) {
321321
Optional<ValidationException> exception = ifFails(entry.getValue(), subject.get(key));
322322
if (exception.isPresent()) {
323-
addValidationException(exception.get().prepend(key));
323+
validationExceptions.add(exception.get().prepend(key));
324324
}
325325
}
326326
}
327327
}
328328
}
329329

330-
private void testPropertyDependencies(final JSONObject subject) {
330+
private void testPropertyDependencies(final JSONObject subject, List<ValidationException> validationExceptions) {
331331
for (String property: propertyDependencies.keySet()) {
332332
if (subject.has(property)) {
333333
for (String mustBePresent : propertyDependencies.get(property)) {
334334
if (!subject.has(mustBePresent)) {
335-
addValidationException(failure(format("property [%s] is required", mustBePresent), "dependencies"));
335+
validationExceptions.add(
336+
failure(format("property [%s] is required", mustBePresent), "dependencies"));
336337
}
337338
}
338339
}
339340
}
340341
}
341342

342-
private void testRequiredProperties(final JSONObject subject) {
343+
private void testRequiredProperties(final JSONObject subject, List<ValidationException> validationExceptions) {
343344
for (String required:requiredProperties) {
344345
if (!subject.has(required)) {
345-
addValidationException(failure(format("required key [%s] not found", required), "required"));
346+
validationExceptions.add(
347+
failure(format("required key [%s] not found", required), "required"));
346348
}
347349
}
348350
}
349351

350-
private void testSchemaDependencies(final JSONObject subject) {
352+
private void testSchemaDependencies(final JSONObject subject, List<ValidationException> validationExceptions) {
351353
for (Map.Entry<String, Schema> schemaDep : schemaDependencies.entrySet()) {
352354
String propName = schemaDep.getKey();
353355
if (subject.has(propName)) {
354-
ifFails(schemaDep.getValue(), subject).ifPresent(this::addValidationException);
356+
ifFails(schemaDep.getValue(), subject).ifPresent(validationExceptions::add);
355357
}
356358
}
357359
}
358360

359-
private void testSize(final JSONObject subject) {
361+
private void testSize(final JSONObject subject, List<ValidationException> validationExceptions) {
360362
int actualSize = subject.length();
361363
if (minProperties != null && actualSize < minProperties.intValue()) {
362-
addValidationExceptions(asList(failure(format("minimum size: [%d], found: [%d]", minProperties, actualSize),
364+
validationExceptions.addAll(
365+
asList(failure(format("minimum size: [%d], found: [%d]", minProperties, actualSize),
363366
"minProperties")));
364367
return;
365368
}
366369
if (maxProperties != null && actualSize > maxProperties.intValue()) {
367-
addValidationExceptions(asList(failure(format("maximum size: [%d], found: [%d]", maxProperties, actualSize),
370+
validationExceptions.addAll(
371+
asList(failure(format("maximum size: [%d], found: [%d]", maxProperties, actualSize),
368372
"maxProperties")));
369373
}
370374
}
@@ -376,23 +380,23 @@ public void validate(final Object subject) {
376380
throw failure(JSONObject.class, subject);
377381
}
378382
} else {
379-
validationExceptions = null;
383+
List<ValidationException> validationExceptions = new ArrayList<>();
380384
JSONObject objSubject = (JSONObject) subject;
381-
testProperties(objSubject);
382-
testRequiredProperties(objSubject);
383-
testAdditionalProperties(objSubject);
384-
testSize(objSubject);
385-
testPropertyDependencies(objSubject);
386-
testSchemaDependencies(objSubject);
387-
testPatternProperties(objSubject);
388-
testPropertyNames(objSubject);
385+
testProperties(objSubject, validationExceptions);
386+
testRequiredProperties(objSubject, validationExceptions);
387+
testAdditionalProperties(objSubject, validationExceptions);
388+
testSize(objSubject, validationExceptions);
389+
testPropertyDependencies(objSubject, validationExceptions);
390+
testSchemaDependencies(objSubject, validationExceptions);
391+
testPatternProperties(objSubject, validationExceptions);
392+
testPropertyNames(objSubject, validationExceptions);
389393
if (null != validationExceptions) {
390394
ValidationException.throwFor(this, validationExceptions);
391395
}
392396
}
393397
}
394398

395-
private void testPropertyNames(JSONObject subject) {
399+
private void testPropertyNames(JSONObject subject, List<ValidationException> validationExceptions) {
396400
if (propertyNameSchema != null) {
397401
String[] names = JSONObject.getNames(subject);
398402
if (names == null || names.length == 0) {
@@ -402,7 +406,7 @@ private void testPropertyNames(JSONObject subject) {
402406
try {
403407
propertyNameSchema.validate(name);
404408
} catch (ValidationException e) {
405-
addValidationException(e.prepend(name));
409+
validationExceptions.add(e.prepend(name));
406410
}
407411
}
408412
}

core/src/main/java/org/everit/json/schema/Schema.java

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44
import org.json.JSONWriter;
55

66
import java.io.StringWriter;
7-
import java.util.ArrayList;
8-
import java.util.Collection;
9-
import java.util.List;
107
import java.util.Objects;
118

129
/**
@@ -63,8 +60,6 @@ public Builder<S> schemaLocation(String schemaLocation) {
6360

6461
protected final String schemaLocation;
6562

66-
protected List<ValidationException> validationExceptions = null;
67-
6863
/**
6964
* Constructor.
7065
*
@@ -226,18 +221,4 @@ protected ValidationException failure(Class<?> expectedType, Object actualValue)
226221
protected boolean canEqual(final Object other) {
227222
return (other instanceof Schema);
228223
}
229-
230-
protected void addValidationException(ValidationException e) {
231-
if (null == validationExceptions) {
232-
validationExceptions = new ArrayList<>();
233-
}
234-
validationExceptions.add(e);
235-
}
236-
237-
protected void addValidationExceptions(Collection<ValidationException> c) {
238-
if (null == validationExceptions) {
239-
validationExceptions = new ArrayList<>();
240-
}
241-
validationExceptions.addAll(c);
242-
}
243224
}

core/src/main/java/org/everit/json/schema/StringSchema.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -117,23 +117,25 @@ public Pattern getPattern() {
117117
return pattern;
118118
}
119119

120-
private void testLength(final String subject) {
120+
private void testLength(final String subject, List<ValidationException> validationExceptions) {
121121
int actualLength = subject.codePointCount(0, subject.length());
122122
if (minLength != null && actualLength < minLength.intValue()) {
123-
addValidationException(failure("expected minLength: " + minLength + ", actual: "
123+
validationExceptions.add(
124+
failure("expected minLength: " + minLength + ", actual: "
124125
+ actualLength, "minLength"));
125126
}
126127
if (maxLength != null && actualLength > maxLength.intValue()) {
127-
addValidationException(failure("expected maxLength: " + maxLength + ", actual: "
128+
validationExceptions.add(
129+
failure("expected maxLength: " + maxLength + ", actual: "
128130
+ actualLength, "maxLength"));
129131
}
130132
}
131133

132-
private void testPattern(final String subject) {
134+
private void testPattern(final String subject, List<ValidationException> validationExceptions) {
133135
if (pattern != null && !pattern.matcher(subject).find()) {
134136
String message = format("string [%s] does not match pattern %s",
135137
subject, pattern.pattern());
136-
addValidationExceptions(Arrays.asList(failure(message, "pattern")));
138+
validationExceptions.addAll(Arrays.asList(failure(message, "pattern")));
137139
}
138140
}
139141

@@ -144,13 +146,13 @@ public void validate(final Object subject) {
144146
throw failure(String.class, subject);
145147
}
146148
} else {
147-
validationExceptions = null;
149+
List<ValidationException> validationExceptions = new ArrayList<>();
148150
String stringSubject = (String) subject;
149-
testLength(stringSubject);
150-
testPattern(stringSubject);
151+
testLength(stringSubject, validationExceptions);
152+
testPattern(stringSubject, validationExceptions);
151153
Optional<String> failure = formatValidator.validate(stringSubject);
152154
if (failure.isPresent()) {
153-
addValidationException(failure(failure.get(), "format"));
155+
validationExceptions.add(failure(failure.get(), "format"));
154156
}
155157
if (null != validationExceptions) {
156158
ValidationException.throwFor(this, validationExceptions);

core/src/test/java/org/everit/json/schema/ArraySchemaTest.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,6 @@
2121
import org.json.JSONObject;
2222
import org.junit.Test;
2323

24-
import java.util.ArrayList;
25-
import java.util.Arrays;
26-
import java.util.List;
27-
2824
import static org.everit.json.schema.TestSupport.buildWithLocation;
2925
import static org.junit.Assert.assertFalse;
3026
import static org.junit.Assert.assertTrue;
@@ -173,9 +169,7 @@ public void uniqueObjectValues() {
173169
public void equalsVerifier() {
174170
EqualsVerifier.forClass(ArraySchema.class)
175171
.withRedefinedSuperclass()
176-
.withIgnoredFields("schemaLocation", "validationExceptions")
177-
.withPrefabValues(List.class, new ArrayList<ValidationException>(),
178-
Arrays.asList(new ValidationException(NotSchema.builder().mustNotMatch(BooleanSchema.INSTANCE).build(), "msg", "kwd", "loc")))
172+
.withIgnoredFields("schemaLocation")
179173
.suppress(Warning.STRICT_INHERITANCE)
180174
.verify();
181175
}

core/src/test/java/org/everit/json/schema/CombinedSchemaTest.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@
2121
import org.junit.Assert;
2222
import org.junit.Test;
2323

24-
import java.util.ArrayList;
25-
import java.util.Arrays;
2624
import java.util.List;
2725

2826
import static java.util.Arrays.asList;
@@ -118,9 +116,7 @@ public void reportCauses() {
118116
public void equalsVerifier() {
119117
EqualsVerifier.forClass(CombinedSchema.class)
120118
.withRedefinedSuperclass()
121-
.withIgnoredFields("schemaLocation", "validationExceptions")
122-
.withPrefabValues(List.class, new ArrayList<ValidationException>(),
123-
Arrays.asList(new ValidationException(NotSchema.builder().mustNotMatch(BooleanSchema.INSTANCE).build(), "msg", "kwd", "loc")))
119+
.withIgnoredFields("schemaLocation")
124120
.suppress(Warning.STRICT_INHERITANCE)
125121
.verify();
126122
}

0 commit comments

Comments
 (0)