package com.fasterxml.jackson.databind.misc; import java.util.*; import java.util.concurrent.*; import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.BaseMapTest; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; public class RaceCondition738Test extends BaseMapTest { static abstract class AbstractHasSubTypes implements HasSubTypes { } static class TypeOne extends AbstractHasSubTypes { private final String id; public TypeOne(String id) { this.id = id; } @JsonProperty public String getId() { return id; } @Override public String getType() { return TypeOne.class.getSimpleName(); } } @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT) @JsonSubTypes({ @JsonSubTypes.Type(value = TypeOne.class, name = "one") }) public interface HasSubTypes { String getType(); } static class Wrapper { private final HasSubTypes hasSubTypes; private Wrapper(HasSubTypes hasSubTypes) { this.hasSubTypes = hasSubTypes; } @JsonProperty public HasSubTypes getHasSubTypes() { return hasSubTypes; } } /* /********************************************************** /* Test methods /********************************************************** */ public void testRepeatedly() throws Exception { final int COUNT = 2000; for (int i = 0; i < COUNT; i++) { runOnce(i, COUNT); } } void runOnce(int round, int max) throws Exception { final ObjectMapper mapper = getObjectMapper(); Callable<String> writeJson = new Callable<String>() { @Override public String call() throws Exception { Wrapper wrapper = new Wrapper(new TypeOne("test")); return mapper.writeValueAsString(wrapper); } }; int numThreads = 4; ExecutorService executor = Executors.newFixedThreadPool(numThreads); List<Future<String>> jsonFutures = new ArrayList<Future<String>>(); for (int i = 0; i < numThreads; i++) { jsonFutures.add(executor.submit(writeJson)); } executor.shutdown(); executor.awaitTermination(5, TimeUnit.SECONDS); for (Future<String> jsonFuture : jsonFutures) { String json = jsonFuture.get(); JsonNode tree = mapper.readTree(json); JsonNode wrapped = tree.get("hasSubTypes"); if (!wrapped.has("one")) { throw new IllegalStateException("Round #"+round+"/"+max+" ; missing property 'one', source: "+json); } } } private static ObjectMapper getObjectMapper() { return new ObjectMapper(); } }