package com.fasterxml.jackson.failing;

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;
import com.fasterxml.jackson.databind.module.SimpleModule;

public class RaceCondition738Test extends BaseMapTest
{
    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
    @JsonSubTypes({
        @JsonSubTypes.Type(value = TypeOne.class, name = "one"),
        @JsonSubTypes.Type(value = TypeTwo.class, name = "two"),
        @JsonSubTypes.Type(value = TypeThree.class, name = "three")
    })
    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();
        }
    }

    static class TypeTwo extends AbstractHasSubTypes {
        private final String id;
        public TypeTwo(String id) {
            this.id = id;
        }
        @JsonProperty
        public String getId() {
            return id;
        }
        @Override
        public String getType() {
            return TypeTwo.class.getSimpleName();
        }
    }    

    static class TypeThree extends AbstractHasSubTypes {
        private final String id;
        public TypeThree(String id) {
            this.id = id;
        }
        @JsonProperty
        public String getId() {
            return id;
        }
        @Override
        public String getType() {
            return TypeThree.class.getSimpleName();
        }
    }

    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 {
        for (int i = 0; i < 1000; i++) {
            runOnce();
        }
    }
    
    void runOnce() 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("Missing 'one', source: "+json);
            }
        }
    }

    private static ObjectMapper getObjectMapper() {
        SimpleModule module = new SimpleModule("subTypeRace");
        module.setMixInAnnotation(HasSubTypes.class, AbstractHasSubTypes.class);

        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(module);
        return mapper;
    }
}

@@ -284,436 +284,64 @@
 er;%0A
-import com.fasterxml.jackson.databind.module.SimpleModule;%0A%0Apublic class RaceCondition738Test extends BaseMapTest%0A%7B%0A    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)%0A    @JsonSubTypes(%7B%0A        @JsonSubTypes.Type(value = TypeOne.class, name = %22one%22),%0A        @JsonSubTypes.Type(value = TypeTwo.class, name = %22two%22),%0A        @JsonSubTypes.Type(value = TypeThree.class, name = %22three%22)%0A    %7D)
+%0Apublic class RaceCondition738Test extends BaseMapTest%0A%7B
 %0A   
@@ -785,428 +785,109 @@
     
-static class TypeTwo extends AbstractHasSubTypes %7B%0A        private final String id;%0A        public TypeTwo(String id) %7B%0A            this.id = id;%0A        %7D%0A        @JsonProperty%0A        public String getId() %7B%0A            return id;%0A        %7D%0A        @Override%0A        public String getType() %7B%0A            return TypeTwo.class.getSimpleName();%0A        %7D%0A    %7D    %0A%0A    static class TypeThree extends AbstractHas
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)%0A    @Json
 SubTypes
  %7B%0A 
@@ -874,33 +874,33 @@
    @JsonSubTypes
- 
+(
 %7B%0A        privat
@@ -897,314 +897,70 @@
     
-private final String id;%0A        public TypeThree(String id) %7B%0A            this.id = id;%0A        %7D%0A        @JsonProperty%0A        public String getId() %7B%0A            return id;%0A        %7D%0A        @Override%0A        public String getType() %7B%0A            return TypeThree.class.getSimpleName();%0A        %7D
+@JsonSubTypes.Type(value = TypeOne.class, name = %22one%22)
 %0A    %7D
-%0A
+)
 %0A   
@@ -1538,32 +1538,62 @@
 ows Exception %7B%0A
+        final int COUNT = 50;%0A
         for (int
@@ -1608,12 +1608,13 @@
 i %3C 
-1000
+COUNT
 ; i+
@@ -1638,16 +1638,24 @@
 runOnce(
+i, COUNT
 );%0A     
@@ -1687,16 +1687,34 @@
 runOnce(
+int round, int max
 ) throws
@@ -2695,24 +2695,65 @@
 s(%22one%22)) %7B%0A
+System.out.println(%22JSON wrong: %22+json);%0A
             
@@ -2793,378 +2793,232 @@
 on(%22
-Missing 'one', source: %22+json);%0A            %7D%0A        %7D%0A    %7D%0A%0A    private static ObjectMapper getObjectMapper() %7B%0A        SimpleModule module = new SimpleModule(%22subTypeRace%22);%0A        module.setMixInAnnotation(HasSubTypes.class, AbstractHasSubTypes.class);%0A%0A        ObjectMapper mapper = new ObjectMapper();%0A        mapper.registerModule(module);%0A        return mapper
+Round #%22+round+%22/%22+max+%22 ; missing property 'one', source: %22+json);%0A            %7D%0ASystem.out.println(%22JSON fine: %22+json);%0A        %7D%0A    %7D%0A%0A    private static ObjectMapper getObjectMapper() %7B%0A        return new ObjectMapper()
 ;%0A  

Simplify failing test