1 package com.fasterxml.jackson.databind.misc; 2 3 import java.util.*; 4 import java.util.concurrent.*; 5 6 import com.fasterxml.jackson.annotation.*; 7 8 import com.fasterxml.jackson.databind.BaseMapTest; 9 import com.fasterxml.jackson.databind.JsonNode; 10 import com.fasterxml.jackson.databind.ObjectMapper; 11 12 public class RaceCondition738Test extends BaseMapTest 13 { 14 static abstract class AbstractHasSubTypes implements HasSubTypes { } 15 16 static class TypeOne extends AbstractHasSubTypes { 17 private final String id; TypeOne(String id)18 public TypeOne(String id) { 19 this.id = id; 20 } 21 @JsonProperty getId()22 public String getId() { 23 return id; 24 } 25 @Override getType()26 public String getType() { 27 return TypeOne.class.getSimpleName(); 28 } 29 } 30 31 @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT) 32 @JsonSubTypes({ 33 @JsonSubTypes.Type(value = TypeOne.class, name = "one") 34 }) 35 public interface HasSubTypes { getType()36 String getType(); 37 } 38 39 static class Wrapper { 40 private final HasSubTypes hasSubTypes; 41 Wrapper(HasSubTypes hasSubTypes)42 private Wrapper(HasSubTypes hasSubTypes) { 43 this.hasSubTypes = hasSubTypes; 44 } 45 46 @JsonProperty getHasSubTypes()47 public HasSubTypes getHasSubTypes() { 48 return hasSubTypes; 49 } 50 } 51 52 /* 53 /********************************************************** 54 /* Test methods 55 /********************************************************** 56 */ 57 testRepeatedly()58 public void testRepeatedly() throws Exception { 59 final int COUNT = 3000; 60 for (int i = 0; i < COUNT; i++) { 61 runOnce(i, COUNT); 62 } 63 } 64 runOnce(int round, int max)65 void runOnce(int round, int max) throws Exception { 66 final ObjectMapper mapper = newJsonMapper(); 67 Callable<String> writeJson = new Callable<String>() { 68 @Override 69 public String call() throws Exception { 70 Wrapper wrapper = new Wrapper(new TypeOne("test")); 71 return mapper.writeValueAsString(wrapper); 72 } 73 }; 74 75 int numThreads = 4; 76 ExecutorService executor = Executors.newFixedThreadPool(numThreads); 77 List<Future<String>> jsonFutures = new ArrayList<Future<String>>(); 78 for (int i = 0; i < numThreads; i++) { 79 jsonFutures.add(executor.submit(writeJson)); 80 } 81 82 executor.shutdown(); 83 executor.awaitTermination(5, TimeUnit.SECONDS); 84 85 for (Future<String> jsonFuture : jsonFutures) { 86 String json = jsonFuture.get(); 87 JsonNode tree = mapper.readTree(json); 88 JsonNode wrapped = tree.get("hasSubTypes"); 89 90 if (!wrapped.has("one")) { 91 throw new IllegalStateException("Round #"+round+"/"+max+" ; missing property 'one', source: "+json); 92 } 93 } 94 } 95 } 96